[
        {
          "id": "help-api-change-management",
          "title": "API change management",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/api-change-management/",
          "content": "Automatic API changelog\n  Breaking changes identification\n  Changes notification    \n      Slack\n      Email\n      RSS\n      Webhooks\n    \n  \n  GitHub integration\n\n\nAutomatic API changelog\n\nBump automatically builds a changelog for your API. Each time you upload a new version of your API definition, you will have a new event in your changelog. A link to the changelog page is available on each documentation:\n\n\n\nAs you can see below on the Bump API changelog, every changes we made are listed: whether it’s a structural change (endpoint or parameter removed, modified or added for example) or a content change (description or example modification).\n\n\n\nBreaking changes identification\n\nBump automatically identifies when a change is breaking for your API consumers. Here are the changes considered as breaking:\n\n\n  Rename or delete endpoint, unless it was deprecated before\n  Rename or delete a property (body, header or query parameter), unless it was deprecated before\n  Modify the type of a property\n  Set an existing property as required\n  Add or delete a security requirement\n\n\nChanges notification\n\nBump can notify changes via Slack, email, RSS or any custom HTTP webhook.\n\nSlack\n\nEach time your API changes, you can notify your team directly on Slack by activating the Slack integration in your API integrations settings:\n\n\n\nEmail\n\nUsers can subscribe to your API changelog and receive a weekly digest.\n\n\n\nRSS\n\nThe changelog page exposes an RSS feed your users can subscribe to. Here is an example with the Bump API changelog.\n\nWebhooks\n\nYou can define as many webhooks as you wish to receive structural changes when they occur on your documentations. Please check the dedicated page to find out how to setup a webhook.\n\nGitHub integration\n\nWith our Github Action, you can receive automatic API diff comments directly in your pull requests. This pull request comment will include:\n\n\n  a diff summary\n  information about the breaking change state"
        },
        {
          "id": "help-api-change-management-webhooks",
          "title": "Webhooks",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/api-change-management/webhooks/",
          "content": "Setup    \n      Step 1:\n      Step 2:\n      Step 3:\n      Payload content\n    \n  \n  Delete a webhook\n\n\nA webhook is a way for an app to get instant notifications from other applications. Webhooks are automated messages (also called payloads) sent to a specific URL. Unlike a classic call to an API, webhooks are more efficient than just polling it.\n\nWebhooks help integrate Bump into your current workflows. The chosen URLs will receive an HTTP request automatically when Bump notices a structural change in your documentation.\n\nNotifications can be sent to several URLs, allowing better integration with your existing tools.\n\nSetup\n\nStep 1:\n\nSelect the Integrations tab from your Bump dashboard to reach the webhook section.\n\n\n\nStep 2:\n\nAfter adding your webhook, a confirmation will be displayed. From this screen, you can modify the payload URL and find the secret token to be used (which helps securing requests coming from Bump).\n\n\n\nHere is a pseudo-code (in Ruby language) which you should implement in your server if you want to check the authenticity of the received payload:\n\ndef verify_signature(payload_body)\n  signature = \"sha256=\" + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new(\"sha256\"), ENV[\"SECRET_TOKEN\"], payload_body)\n  return halt 500, \"Signatures didn't match!\" unless Rack::Utils.secure_compare(signature, request.env[\"HTTP_X_BUMP_SIGNATURE_256\"])\nend\n\n\nStep 3:\n\nThe Recent deliveries section include a **Test Webhook **element that helps you verify that the webhook works correctly. You can also check the 10 last notifications.\n\n\n\nPayload content\n\nDetails about the payload content sent for each setup webhooks is available in our absolutely gorgeous API documentation.\n\nDelete a webhook\n\nTo stop receiving notifications, you can delete a webhook by selecting it from the Integrations section and then confirm the deletion in the Danger zone."
        },
        {
          "id": "help-changes-management-changelog",
          "title": "Changelog",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/changes-management/changelog/",
          "content": "Changelog entries\n  Compare changelog entries\n  Email &amp; RSS notifications    \n      How frequently emails are sent?\n    \n  \n\n\nThe changelog allows you to follow what’s happening on your API, or on your API documentation.\n\nChangelog entries\n\nWith each new release of an API document, the changelog is automatically updated to inform your API consumers of every change. You can also add context during a release or later.\n\nEach release generates a new entry in the changelog, summarizing the key changes since the previous one.\n\n\n\nTo provide more details or context to your API consumers, you can add a title and description to a release during deployment: these elements then appear in the changelog entry.\n\nA simple color-coding system provides a quick visual indication of changes. Items in green have been added, items in orange have been modified, and items in red have been deleted.\n\nWhen a change is considered a breaking change, it appears in red at the top of the changelog entry.\n\n\n\nCompare changelog entries\n\nFrom the changelog, you can select two entries, including between two different branches, and compare them instantly.\n\nOn the change comparison page, each entry is identifiable by its release date and Bump.sh branch name.\n\n\n\nAfter selecting the two versions to compare, a detailed list of changes will appear.\n\n\n\nYou have the option to share the URL of this comparison directly with your API consumers or team members for reference if needed.\n\nEmail &amp; RSS notifications\n\nYour API consumers can subscribe to a weekly summary of changes to your API via email by clicking on Get Updates from the changelog page.\nTwo options are available: email notification or RSS feed.\n\nHow frequently emails are sent?\n\nTo avoid sending too many emails to your API consumers, change notifications are sent no more than once a week by email:\n\n  If there are one or more changes per week, a recap email is sent a week after the last recap was sent.\n  If the API documentation hasn’t been updated for more than a week, a recap email is sent the day after a new change is added.\n\n\n\n  The emails collected in this way will only be used for sending this changelog summary and nothing else."
        },
        {
          "id": "help-changes-management",
          "title": "Changes management",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/changes-management/",
          "content": "What is a change\n  How to manage and track changes\n\n\nTracking API changes is essential, both for API builders and consumers.\n\nWhat is a change\n\nA change is a modification of your API with an impact on its behavior.\nChanges are differentiated into various categories:\n\n\n  \n    \n      Documentation change\n      a description is modified\n    \n    \n      Structural change\n      the structure of the API is modified\n    \n    \n      Breaking change\n      a major change in your API  requires an update to the code consuming it\n    \n  \n\n\nHow to manage and track changes\n\nChangelog is a crucial feature of Bump.sh, recording the history of all deployments and detailing changes compared to previous versions.\n\nThe changes comparison allows you to compare two deployed API definitions, regardless of differences in dates or branches.\n\nBump.sh offers various notification options, whether it’s integration into your existing workflows or email notifications, you can be confident that you’ll stay informed about important changes."
        },
        {
          "id": "help-changes-management-integrations",
          "title": "Integrations",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/changes-management/integrations/",
          "content": "Slack integration\n  Webhooks    \n      Setup\n      Payload content\n      Delete a webhook\n    \n  \n\n\nSlack integration\n\nBump.sh integration with Slack allows you to notify team members working with your API of its latest changes, through dedicated channels, for example.\n\n\n  From your Slack workspace admin, create a webhook: this allows you to specify where Bump.sh should send change notifications.\n  Once you have copied the webhook URL, go to the settings of your documentation, in the Integrations tab, enable the Slack option, and provide the previously obtained webhook URL.\n  Mention the channels where you want to receive notifications.\n\n\nAfter configuration, remember to test the proper reception of notifications with the test button.\n\n\n\nWebhooks\n\nWhen your API evolves, you may want to automatically trigger some related tasks: rebuilding your API client, updating your security test suite, your integration tests, etc.\n\nThis can be achieved through Bump.sh webhooks.\nIt will send a specific request to the URLs you have configured every time it detects a change in your API structure.\nYou can define as many webhooks as you need.\n\nNotifications can be sent to several URLs, allowing better integration with your existing tools.\n\nSetup\n\nWebhooks can be configured from the Integrations tab in your documentation settings.\n\n\n\nAfter adding your webhook, a confirmation will be displayed. From this screen, you can modify the payload URL and find the secret token to be used (which helps securing requests coming from Bump).\n\n\n\nHere is a pseudo-code (in Ruby language) which you should implement in your server if you want to check the authenticity of the received payload:\n\ndef verify_signature(payload_body)\n  signature = \"sha256=\" + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new(\"sha256\"), ENV[\"SECRET_TOKEN\"], payload_body)\n  return halt 500, \"Signatures didn't match!\" unless Rack::Utils.secure_compare(signature, request.env[\"HTTP_X_BUMP_SIGNATURE_256\"])\nend\n\n\nFinally, you can check from this same page if your setup is working correctly by clicking on the Test webhook button.\n\nPayload content\n\nDetails about the payload content sent for each setup webhooks is available in our Bump.sh API documentation.\n\nDelete a webhook\n\nTo stop receiving notifications, you can delete a webhook by selecting it from the Integrations section and then confirm the deletion in the Danger zone."
        },
        {
          "id": "help-continuous-integration-api",
          "title": "API",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/continuous-integration/api/",
          "content": "Deploy to your documentation\n  Diff between two API documents\n  Manage your branches\n  List your Hubs\n  Documentation Change Webhook\n\n\nIf neither the Github-Action or the CLI are sufficient for you, please read this page to discover what you can do with the Bump.sh API.\n\nDeploy to your documentation\n\nYou can use the POST /versions endpoint to publish a new API document to your documentation.\n\nYou will need to provide at least the following request body parameters:\n\n  a documentation slug of your target Bump.sh documentation\n  a definition content of your API document\n\n\nThe endpoint gives you some other optional parameters such as:\n\n  a branch_name if you want to deploy to a different branch\n  a list of references if you have filesystem based external references in your root definition\n\n\nDiff between two API documents\n\nYou can use the POST /diffs endpoint to compare two given API documents. To see the diff result you will then need to call the GET /diffs/:id endpoint with the id returned by the POST request.\n\nFor file comparison, you will need to provide at least the following request body parameters:\n\n  a previous_definition content\n  a definition content\n\n\nFor URLs comparison, you will need:\n\n  a previous_url pointing to an online API document\n  a url pointing to an other online API document\n\n\nThe endpoint gives you some other optional parameters such as:\n\n  a list of previous_references if you have filesystem based external references in your root previous_definition\n  a list of references if you have filesystem based external references in your root definition\n\n\nManage your branches\n\nThanks to the Branches endpoints you can:\n\n\n  List existing branches of your documentation\n  Create a new branch\n  Delete a branch\n  Promote a branch as the “default” one. Warning: this will affect your published documentation and make the given branch the default one visible by your users.\n\n\nList your Hubs\n\nThanks to the Hubs endpoints you can:\n\n\n  Fetch information of an existing Hub including the list of APIs it contains.\n\n\nDocumentation Change Webhook\n\nOur API offers a “documentation change” webhook, which can help you receive automated notifications whenever one of your documentation receives a new published API document.\n\nFor more information on how to use this webhook please check the dedicated help page."
        },
        {
          "id": "help-continuous-integration-azure-devops",
          "title": "Azure DevOps",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/continuous-integration/azure-devops/",
          "content": "Generate your API documentation &amp; changelog\n  Azure DevOps YAML Pipelines vs. Classic Pipelines\n  Setting up Bump.sh with an Azure DevOps YAML Pipeline    \n      Step 1: Setup Azure Organization &amp; Project\n      Step 2: Configure Your YAML Pipeline\n      Step 3: Setup Variables\n      Step 4: Save and Commit the YAML File\n      Step 5: Diff Detection Comments\n    \n  \n  Troubleshooting\n\n\nGenerate your API documentation &amp; changelog\n\nAzure DevOps has a feature called “Pipelines”, which allow you to chain together various actions for Continuous Integration &amp; Continuous Delivery. In this tutorial we’ll show you how to create a pipeline, and use it to deploy your Bump.sh API documentation when new commits are pushed to your main Git branch, and show how to check for breaking changes on pull requests.\n\nAzure DevOps YAML Pipelines vs. Classic Pipelines\n\nAzure DevOps supports two distinct pipeline formats, the old “Classic Pipelines” built and managed via a drag-and-drop interface, and “YAML Pipelines” which are configured entirely via a YAML file in the repo.\n\nSeeing as YAML Pipelines are the newer approach, and many people may have never worked with Classic Pipelines, we’ll stick with YAML Pipelines for this tutorial. This also has the benefit of being entirely Git controlled, so you can change your pipelines along with your OpenAPI and all the API source-code all at once.\n\nSetting up Bump.sh with an Azure DevOps YAML Pipeline\n\nStep 1: Setup Azure Organization &amp; Project\n\nSign in to your Azure organization if you have one, or sign up.\n\nThen navigate to an existing project, or create a new one if you need to.\n\n\n\nClick on the project, then click Pipelines, and New Pipeline to get the whole thing going.\n\n\n\nSelect where your code is hosted. You can choose from Azure Repos Git, GitHub, Bitbucket, or other repositories. For this example, we’ll assume you are using Azure Repos Git.\n\nStep 2: Configure Your YAML Pipeline\n\nChoose YAML when prompted to configure the pipeline, and select the repository branch you want the pipeline to monitor (e.g., main).\n\nThen you’ll be presented with lots of options for start templates. Click “Node.js” because Bump.sh CLI is built with Node.\n\n\n\nNow we can override the YAML Pipeline they’ve popped in the repo with our own. You can do this by cloning the new repository, or by pulling the commits on an exist repository.\n\nEither way, let’s update azure-pipelines.yml with the following:\n\n# azure-pipelines.yml\ntrigger:\n  branches:\n    include:\n      - '*'\n\nvariables: \n- group: bumpsh\n\n  # Set to the location of the main OpenAPI document relative to repo root\n- name: openApiDoc\n  value: openapi.yaml\n\n  # The Documentation ID or Slug found in Bump.sh API Settings\n- name: bumpDoc\n  value: azure-demo\n\npool:\n  vmImage: ubuntu-latest\n  demands: \n    - npm\n\njobs:\n  - job: openapi_diff\n    # Only run this task if build was triggered by a commit to a branch other than main\n    condition: and(succeeded(), ne(variables['build.sourceBranch'], 'refs/heads/main'))\n    steps:\n      - script: npx bump-cli deploy --dry-run $(openApiDoc) --doc $(bumpDoc) --token=\"$BUMP_TOKEN\"\n        displayName: Validate OpenAPI\n\n      - script: npx bump-cli diff --format markdown $(openApiDoc) --doc $(bumpDoc) --token=\"$BUMP_TOKEN\" &gt; bump-diff.md\n        displayName: Diff OpenAPI\n\n      - task: PullRequestComment@1\n        inputs:\n          markdownFile: $(Build.SourcesDirectory)/bump-diff.md\n        displayName: PR Comment\n\n  - job: openapi_deploy\n    # Only run this task if build was triggered by a commit to the main branch\n    condition: and(succeeded(), eq(variables['build.sourceBranch'], 'refs/heads/main'))\n    steps:\n      - script: |\n          npx bump-cli deploy  $(openApiDoc) --doc $(bumpDoc) --token=\"$BUMP_TOKEN\"\n        displayName: Deploy API Documentation\n\n\nLet’s walk through a few key bits here.\n\n\n  Trigger: This pipeline triggers automatically whenever changes are made to any branch. Later conditions will decide which jobs to do.\n  Pool: Specifies that the pipeline should use the latest available Ubuntu agent, and it’ll need to have npm installed.\n  Jobs: Jobs have a condition, and if it passes it will run the steps. In this example we’re making the condition check the branch is main and running deployment, or checking the branch is something else, and doing a diff on the OpenAPI to check for breaking changes.\n\n\nDeployment is handled by the command npx bump-cli deploy which uses NPM’s npx command to avoid needing to run npm install in each step, and will grab the bump-cli NPM package so you can use the CLI on the fly.\n\nStep 3: Setup Variables\n\nVariables can be set several ways in Azure DevOps, and we’re using two different approaches for publicly knowable variables and secrets. Here we’re setting a variable called openApiDoc to point to the main OpenAPI document, and bumpDoc which contains the ID or slug of the API Documentation on Bump.sh to be deployed to or checked against.\n\nvariables: \n- group: bumpsh\n\n  # Set to the location of the main OpenAPI document relative to repo root\n- name: openApiDoc\n  value: openapi.yaml\n\n  # The Documentation ID or Slug found in Bump.sh API Settings\n- name: bumpDoc\n  value: azure-demo\n\n\nThe variable group in there refers to a group of environment variables, and these are defined in the Azure DevOps interface: Pipelines &gt; Library and click on + Variable Groups.\n\n\n\nCreate a new group with the name bumpsh, which matches the group: bumpsh we defined in the workflow.\n\nIn another browser tab, pop over to your Bump.sh API’s Documentation settings, go to the “CI deployment” section, and grab your API key.\n\nBack in the Azure variable group page, create a new variable called BUMP_TOKEN, paste in the API key, and click the lock icon to make it secret.\n\n\n\nFor security reasons, referencing the variable group in the YAML workflow is not enough to give the pipeline access to the variables. This requires one more step: click “Pipeline permissions” and select your pipeline.\n\n\n\nNow we should be good to go!\n\nStep 4: Save and Commit the YAML File\n\nMake sure the pipeline file is saved as azure-pipelines.yml in the root of your repository, commit, and push.\n\nWhen pushing to a branch other than main, you should see output like this in the Pipelines section of the Azure Devops interface, letting you know if the OpenAPI was valid or not.\n\n\n\nWhen pushing to the main you will trigger the deploy job, which will update your hosted Bump.sh documentation if any changes are detected, which will look like this:\n\n\n\nHere we can see the documentation has been created, and there’s even a link to go and see it.\n\nStep 5: Diff Detection Comments\n\nOne of the most powerful features in Bump.sh is the automated changelog, and this logic can be used to speed up API Design Reviews by showing people clearly in a single comment what has changed in a pull request. Instead of having to look at lots of YAML, you can see a summary of all meaningful changes added as a comment.\n\n\n\nThis is powered by the wonderful PR Comment Task which you will need to install for your Azure DevOps Organization.\n\nYou may need to add Contribute to pull requests permission to your Project Collection Build Service Accounts from Project Settings &gt; Repositories &gt; Security, then you should be good to go.\n\nIf you don’t want to have the change detection comments, just delete the following section from azure-pipeline.yaml.\n\n  - script: npx bump-cli diff --format markdown $(openApiDoc) --doc $(bumpDoc) --token=\"$BUMP_TOKEN\" &gt; bump-diff.md\n    displayName: Diff OpenAPI\n\n  - task: PullRequestComment@1\n    inputs:\n      markdownFile: $(Build.SourcesDirectory)/bump-diff.md\n    displayName: PR Comment\n\n\nTroubleshooting\n\nIf this is a new Azure DevOps account you might have problems with:\n\n\n  “No hosted parallelism has been purchased or granted.”\n\n\nYou can request free pipeline runs by entering a name and address in this web form. It says it might take 2-3 days but it’s usually closer to 1."
        },
        {
          "id": "help-continuous-integration-circle-ci",
          "title": "Circle CI",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/continuous-integration/circle-ci/",
          "content": "Bump.sh continuous integration examples\n  Generate your API documentation with Circle CI\n\n\nBump.sh continuous integration examples\n\nBump.sh offers a lot of flexibility to let you configure how it should interact with your CI pipelines.\n\nAll our generic build processes are stored in a dedicated public repository called bump-ci-example. Those processes are defined thanks to dedicated configuration files and scripts which uses the Bump.sh CLI under the hood.\n\nFor more advanced needs, consider using our API.\n\nGenerate your API documentation with Circle CI\n\nThe CircleCI example we provide helps to validate your definition file on pull requests, and then deploy a definition file change merged in your main branch.\n\nCopy-paste the following directory in your own project to see the builds running:\n\n  .circleci/ directory\n\n\n\n  If you encounter an issue or have suggestions on those examples, please do file a ticket on the dedicated repo we would love to hear your feedback."
        },
        {
          "id": "help-continuous-integration-cli",
          "title": "Bump CLI",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/continuous-integration/cli/",
          "content": "Installation    \n      Global installation\n      Add Bump.sh to your Node project\n      Can I install Bump.sh CLI without using NodeJS?\n    \n  \n  Usage    \n      Prepare your Bump.sh account\n    \n  \n  Commands    \n      The deploy command        \n          Deploy a folder all at once\n          Validate an API document\n          Deploy a workflow document on your MCP server\n        \n      \n      The diff command        \n          Public API diffs\n          GitHub Integration\n          Authenticated diffs related to your Bump.sh documentation\n        \n      \n      The preview command        \n          Live preview\n        \n      \n      The overlay command\n    \n  \n  License\n  Contributing\n\n\nThe Bump.sh CLI is used to interact with API documentation and hubs hosted on Bump.sh from your choice of popular API description formats: OpenAPI, Swagger, or AsyncAPI.\n\nUsing OpenAPI (v3.x and v2.0) or AsyncAPI (2.x), you can do any of the following:\n\n\n  Validate an API document before publishing to your documentation.\n  Publish an API document to your Bump.sh documentation or hubs.\n  Compare two API documents to generate a human-readable diff from your API definition.\n\n\nUnder the hood, it uses the API of developers.bump.sh. And is built with the oclif framework in Typescript.\n\n\n  You can use the CLI to interact with your MCP server:\nsee how to deploy a workflow document on your MCP server.\n\n\nInstallation\n\nThe Bump.sh CLI is a node package currently distributed via NPM. This means you must have the Node v20+ interpreter installed on your computer or CI servers.\n\nIf you are looking to use Bump.sh in a continuous integration environment you might be interested by our Github Action.\n\n\n  You can download a standalone package directly from the latest\nGithub release assets if you don’t use Node.\n\n\nGlobal installation\n\nTo install it globally, run the following command with NPM:\n\nnpm install -g bump-cli\n\n\nOr, with Yarn via:\n\nyarn global add bump-cli\n\n\nAdd Bump.sh to your Node project\n\nAs our CLI is a node package, you can easily embed it to your project by adding the package to your package.json file, either with NPM:\n\nnpm install --save-dev bump-cli\n\n\nOr with Yarn via:\n\nyarn add --dev bump-cli\n\n\nYou can then use any Bump.sh commands with npx (same as npm exec):\n\nnpx bump --help\n\n\nCan I install Bump.sh CLI without using NodeJS?\n\nUnfortunately, at the moment we only support the Node environment. However, you can download a standalone package directly from the latest Github release assets which you can run as a standalone binary. Or you can push your documentation using our API (advanced usage only).\n\nUsage\n\nTo list all the available commands, just type bump in your command line environment.\n\n$ bump --help\nThe Bump.sh CLI is used to interact with your API documentation hosted on Bump.sh by using the API of developers.bump.sh\n\nVERSION\n  bump-cli/2.9.12 linux-x64 node-v20.18.1\n\nUSAGE\n  $ bump [COMMAND]\n\nCOMMANDS\n  deploy   Create a new version of your documentation from the given file or URL.\n  diff     Get a comparison diff with your documentation from the given file or URL.\n  help     Display help for bump.\n  overlay  Apply an OpenAPI specified overlay to your API definition.\n  preview  Create a documentation preview from the given file or URL.\n\n\nYou can also get some help anytime by adding --help to any command. Example: bump deploy --help.\n\nPrepare your Bump.sh account\n\nWhile some commands don’t need any API token (preview or diff) you will need an access key if you want to interact with your Bump.sh documentation.\n\nHead over to your Documentation settings in the “CI deployment” section or your Account or Organization settings in the “API keys” section to fetch a personal token for later usage.\n\nCommands\n\n\n  bump deploy [FILE]\n  bump diff [FILE]\n  bump preview [FILE]\n  bump overlay [DEFINITION_FILE] [OVERLAY_FILE]\n\n\nThe deploy command\n\nWhen an API is updated, the documentation should be updated at the same time. This is what the deploy command is for.\n\nbump deploy path/to/api-document.yml --doc my-documentation --token $DOC_TOKEN\n\n\n\n  You can find your own my-documentation slug and $DOC_TOKEN api key from your documentation settings.\n\n\nYou can also deploy a given API document to a different branch of your documentation with the --branch &lt;branch-name&gt; parameter. Please note the branch will be created if it doesn’t exist. More details about the branching feature are available on this dedicated help page. E.g. deploy the API document to the staging branch of the documentation:\n\nbump deploy path/to/api-document.yml --doc my-documentation --token $DOC_TOKEN --branch staging\n\n\n\n  You can also use this command to interact with your MCP server, see how to deploy a workflow definition.\n\n\nDeploy a folder all at once\n\nIf you already have a hub in your Bump.sh account, you can automatically create documentation and deploy it into that hub by publishing a whole directory containing multiple API documents in a single command:\n\nbump deploy dir/path/to/apis/ --auto-create --hub my-hub --token $HUB_TOKEN\n\n\n\n  You can find your own my-hub slug and $HUB_TOKEN api key from your hub settings.\n\n\nPlease note, by default, only files named {slug}-api.[format] are published. Where {slug} is a name for your API and [format] is either yaml or json. Adjust to your file naming convention using the --filename-pattern &lt;pattern&gt; option.\n\nNote that it can include * wildcard special character, but must include the {slug} filter to extract your documentation’s slug from the filename. The pattern can also have any other optional fixed characters.\n\nHere’s a practical example. Let’s assume that you have the following files in your path/to/apis/ directory:\n\npath/to/apis\n└─ private-api-users-service.json\n└─ partner-api-payments-service.yml\n└─ public-api-contracts-service.yml\n└─ data.json\n└─ README.md\n\n\nIn order to deploy the 3 services API definition files from this folder (private-api-users-service.json, partner-api-payments-service.yml and public-api-contracts-service.yml), you can execute the following command:\n\nbump deploy path/to/apis/ --hub my-hub --filename-pattern '*-api-{slug}-service'\n\n\nValidate an API document\n\nSimulate your API document’s deployment to ensure it is valid by adding the --dry-run flag to the deploy command. It is handy in a Continuous Integration environment running a test deployment outside your main branch:\n\nbump deploy path/to/api-document.yml --dry-run --doc my-documentation --token $DOC_TOKEN\n\n\nPlease check bump deploy --help for more usage details.\n\nDeploy a workflow document on your MCP server\n\nUse the bump deploy command with the --mcp-server flag to deploy a Flower or Arazzo workflow document.\n\nbump deploy path/to/flower-document.yml --mcp-server my-mcp-server-id-or-slug --token $BUMP_TOKEN\n\n\n\n  You can find your mcp-server-id-or-slug and $BUMP_TOKEN API key in your MCP server settings.\n\n\nThis feature is currently in closed beta.\nRequest an early access at hello@bump.sh\n\nThe diff command\n\nUsing the diff command can help to spot differences between the local API\ndocument and the latest deployed version.\n\nPublic API diffs\n\nFrom any two API documents or URLs, you can retrieve a comprehensive changelog\nof what has changed between them.\n\n$ bump diff path/to/your/file.yml path/to/your/second_file.yml\n* Comparing the two given definition files... done\nModified: GET /consommations\n  Response modified: 200\n    [Breaking] Body attribute modified: energie\n\n\nBy default the command will always exit with a successful return code. If you\nwant to use this command in a CI environment and want the command to fail in\ncase of a breaking change, you will need to add the --fail-on-breaking flag\nto your diff command.\n\nBy default if the environment variable CI=1 is present (in most continuous\nintegration environment), the flag will be enabled. In that case you can disable\nthe failures with --no-fail-on-breaking flag.\n\nYou can also test this feature in our dedicated web application at\nhttps://api-diff.io/.\n\nGitHub Integration\n\nIf you want to receive automatic bump diff results on Github Pull Requests you\nmight be interested by our Github\nAction\nwhich has support for the diff command.\n\nAuthenticated diffs related to your Bump.sh documentation\n\nFrom an existing Bump.sh documentation, the diff command will retrieve a\ncomparison changelog between your latest published documentation and the given\nfile or URL:\n\nbump diff path/to/your/file.yml --doc my-documentation --token $DOC_TOKEN\n\n\nIf you want to compare two unpublished versions of your API document, the diff command can retrieve a comparison changelog between two given file or URL, “as simple as git diff”:\n\nbump diff path/to/your/file.yml path/to/your/next-file.yml --doc my-documentation --token $DOC_TOKEN\n\n\nPlease check bump diff --help for full usage details.\n\nThe preview command\n\nWhen writing documentation, you might want to preview how it renders on Bump.sh.\nThis is precisely the goal of the preview command: it will create temporary\ndocumentation with a unique URL, which will be available for a short period (30\nminutes).\n\nUsage from a local OpenAPI or AsyncAPI document:\n\nbump preview path/to/file.json\n\n\nYou can also preview a document available via a URL:\n\nbump preview https://developers.bump.sh/source.yaml\n\n\nLive preview\n\nBy using the --live flag you can stay focused on API design (OpenAPI or AsyncAPI file) while seeing a continuously updated preview each time you save your API document.\n\n\n  Launch the live preview command in your terminal\n\n\nbump preview --live --open api-document.yaml\n\n\n\n  Edit your api-document.yaml file in your favorite text editor.\n  Watch the live preview being updated each time you save your file.\n  The additional --open flag helps to automatically open the preview URL in your browser.\n\n\n\n  You can create as many previews as you like without being authenticated. This is a free and unlimited service.\n\n\nPlease check bump preview --help for more usage details\n\nThe overlay command\n\nThe Overlay Specification from the OpenAPI Initiative makes it possible to modify the content of an API definition by adding a layer on top of it. That layer helps adding, removing or changing some or all of the content of the original definition.\n\nThe bump overlay command takes an original API document (an OpenAPI or AsyncAPI document), applies the changes from the overlay document, and outputs a modified version. No changes are made directly to the original document.\n\nbump overlay api-document.yaml overlay.yaml\n\n\nTo redirect the output of the command to a new file you can run:\n\nbump overlay api-document.yaml overlay.yaml &gt; modified-api-document.yaml\n\n\nYou can also apply the overlay using the deploy command with the --overlay flag:\n\nbump deploy api-document.yaml --doc my-doc --token my-token --overlay overlay.yaml\n\n\nIf multiple overlay documents need to be applied, the --overlay parameter can be passed multiple times.\n\nbump deploy api-document.yaml \\\n  --doc my-doc \\\n  --token my-token \\\n  --overlay overlay1.yaml \\\n  --overlay overlay2.yaml\n\n\nLicense\n\nThe Bump CLI project is released under the MIT License.\n\nContributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/bump-sh/cli. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct."
        },
        {
          "id": "help-continuous-integration-github-actions",
          "title": "GitHub Action",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/continuous-integration/github-actions/",
          "content": "Generate your API documentation &amp; changelog\n  Usage    \n      Deploy documentation &amp; diff on pull requests\n      Deploy documentation only\n      Diff on pull requests only\n      Deploy a single documentation on a hub\n      Deploy multiple documentation at once on a hub\n      Deploy a workflow document for your MCP server\n    \n  \n  Inputs\n  Contributing\n  License\n  Code of Conduct\n\n\nGenerate your API documentation &amp; changelog\n\nBump.sh helps you build a branded single source of truth, cataloging all your APIs. We’ve created the reference point for teams consuming and building APIs, no matter which technology they rely on.\n\nBump.sh keeps your API docs always synchronized with your codebase. With this Github Action your API reference is automatically generated - with changelog and diff - on Bump.sh from any OpenAPI or AsyncAPI file.\n\nUsage\n\nStart with creating a documentation on Bump.sh.\n\nOnce you’ve set up an API Documentation, go to Settings &gt; CI Deployment, copy the access token, then add it to GitHub Settings &gt; Secrets &gt; Actions as a new repository secret called BUMP_TOKEN.\n\nThen you can pick from one of the three following API workflow files.\n\n\n  Recommended: Deploy documentation &amp; diff on pull requests\n  Deploy documentation only\n  Diff on pull requests only\n\n\n\n  You can use the GitHub Action to interact with your MCP server:\nsee how to deploy a workflow document on your MCP server.\n\n\nDeploy documentation &amp; diff on pull requests\n\nThis is the recommended workflow, which will create two steps in your automation flow: a validation &amp; diff step on code reviews, followed by a deployment step on merged changes.\n\n.github/workflows/bump.yml\n\nname: Check &amp; deploy API documentation\n\non:\n  push:\n    branches:\n      - main\n\n  pull_request:\n    branches:\n      - main\n\npermissions:\n  contents: read\n  pull-requests: write\n\njobs:\n  deploy-doc:\n    if: ${{ github.event_name == 'push' }}\n    name: Deploy API documentation on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;BUMP_DOC_ID&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: doc/api-documentation.yml\n\n  api-diff:\n    if: ${{ github.event_name == 'pull_request' }}\n    name: Check API diff on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n      - name: Comment pull request with API diff\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;BUMP_DOC_ID&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: doc/api-documentation.yml\n          command: diff\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n\n\n\n  Make sure you adapt the name of the branch your deployment will target, aka your destination branch if relevant (main in the example above), replace &lt;BUMP_DOC_ID&gt; with your Bump.sh documentation slug or id, and point file: to your local API definition file (doc/api-documentation.yml).\n\n\nDeploy documentation only\n\nIf you only need to deploy documentation changes on push, then you can use this workflow instead:\n\n.github/workflows/bump-deploy.yml\n\nname: Deploy documentation\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  deploy-doc:\n    name: Deploy API doc on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;BUMP_DOC_ID&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: doc/api-documentation.yml\n\n\n\n  Make sure you adapt the name of the branch your deployment will target, aka your destination branch if relevant (main in the example above), replace &lt;BUMP_DOC_ID&gt; with your Bump.sh documentation slug or id, and point file: to your local API definition file (doc/api-documentation.yml).\n\n\nDiff on pull requests only\n\nIf you only want to have API diff posted on pull requests, use this workflow:\n\n.github/workflows/api-diff.yml\n\nname: API diff\n\npermissions:\n  contents: read\n  pull-requests: write\n\non:\n  pull_request:\n    branches:\n      - main\n\njobs:\n  api-diff:\n    name: Check API diff on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n      - name: Comment pull request with API diff\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;BUMP_DOC_ID&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: doc/api-documentation.yml\n          command: diff\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n\n\n\n  Make sure you adapt the name of the branch your deployment will target, aka your destination branch if relevant (main in the example above), replace &lt;BUMP_DOC_ID&gt; with your Bump.sh documentation slug or id, and point file: to your local API definition file (doc/api-documentation.yml).\n\n\nDeploy a single documentation on a hub\n\nYou can deploy a documentation inside a hub by adding a hub slug or id.\nNote that the documentation will be automatically created if it doesn’t exist by using the slug you defined with the doc: input.\n\n.github/workflows/bump-deploy.yml\n\nname: Deploy documentation\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  deploy-doc:\n    name: Deploy API doc on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;BUMP_DOC_ID&gt;\n          hub: &lt;BUMP_HUB_ID&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: doc/api-documentation.yml\n\n\n\n  Make sure you adapt the name of the branch your deployment will target, aka your destination branch if relevant (main in the example above), replace &lt;BUMP_DOC_ID&gt; with your Bump.sh documentation slug or id, &lt;BUMP_HUB_ID&gt; with your Bump.sh hub slug or id and point file: to your local API definition file (doc/api-documentation.yml).\n\n\nDeploy multiple documentation at once on a hub\n\nYou can deploy multiple documentation inside a hub from a source directory by adding a hub slug or id and specifying a directory name in the file: input. Note that documentation will be automatically created if they don’t exist.\n\n.github/workflows/bump-deploy.yml\n\nname: Deploy documentation\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  deploy-doc:\n    name: Deploy API doc on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          hub: &lt;BUMP_HUB_ID&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: docs/\n\n\n\n  Make sure you adapt the name of the branch your deployment will target, aka your destination branch if relevant (main in the example above), replace &lt;BUMP_HUB_ID&gt; with your Bump.sh hub slug or id and point file: to your local API definition file folder (docs/).\n\n\nPlease note, by default, only files named {slug}-api.[format] are deployed. Where {slug} is a name for your API and [format] is either yaml or json. Adjust to your file naming convention using the filename_pattern: input.\n\nThe pattern can include * wildcard special character, but must include the {slug} filter to extract your documentation’s slug from the filename. The pattern can also have any other optional fixed characters.\n\nHere’s a practical example. Let’s assume that you have the following files in your path/to/apis/ directory:\n\npath/to/apis\n└─ private-api-users-service.json\n└─ partner-api-payments-service.yml\n└─ public-api-contracts-service.yml\n└─ data.json\n└─ README.md\n\n\nIn order to deploy the 3 services API definition files from this folder (private-api-users-service.json, partner-api-payments-service.yml and public-api-contracts-service.yml), you can use the action like this:\n\n[...]\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          hub: &lt;BUMP_HUB_ID&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: docs/\n          filename_pattern: '*-api-{slug}-service'\n\n\nDeploy a workflow document for your MCP server\n\nReplace BUMP_MCP_SERVER_ID_OR_SLUGwith the slug (or id) of your MCP server. It can be found in your MCP server settings. Don’t forget to replace the file path with the path to your Flower or Arazzo workflow document.\n\n.github/workflows/bump.yml\n\nname: Deploy workflow document for your MCP server\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  deploy-workflow-document:\n    name: Deploy workflow document for MCP server on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n      - name: Deploy workflow document\n        uses: bump-sh/github-action@v1\n        with:\n          command: deploy\n          mcp_server: &lt;BUMP_MCP_SERVER_ID_OR_SLUG&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: doc/flower-document.yml\n\n\nThis feature is currently in closed beta.\nRequest an early access at hello@bump.sh\n\nInputs\n\n\n  \n    doc (required unless you deploy a directory on a hub): Documentation slug or id. Can be found in the documentation settings on your API dashboard.\n  \n  \n    token (required): Do not add your documentation token here, but create an encrypted secret that holds your documentation token.\n\n    \n      Your Bump.sh token can be found in the documentation settings on your API dashboard. Copy it for later usage.\n      In your GitHub repository, go to your “Settings”, and then “Secrets”.\n      Click the button “New repository secret”, name the secret BUMP_TOKEN and paste your Bump.sh token in the value field.\n    \n  \n  \n    file: Relative path to the API definition file or the API definition file folder. Default: api-contract.yml\n  \n  \n    hub (optional): Hub slug or id. Needed when deploying documentation in a Hub. Can be found in the hub settings on your API dashboard.\n  \n  \n    branch (optional): Branch name used during deploy or diff commands. This can be useful to maintain multiple API reference history and make it available in your API documentation.\n  \n  \n    command: Bump.sh command to execute. Default: deploy\n\n    \n      deploy: deploy a new version of the documentation (or MCP server ✨)\n      diff: automatically comment your pull request with the API diff\n      dry-run: dry-run a deployment of the API definition file\n      preview: create a temporary preview\n    \n  \n  \n    expires (optional): Specify a longer expiration date for public diffs (defaults to 1 day). Use iso8601 format to provide a date, or you can use never to keep the result live indefinitely.\n  \n  \n    fail_on_breaking (optional): Mark the action as failed when a breaking change is detected with the diff command. This is only valid with diff command.\n  \n  \n    mcp_server (required to deploy workflow documents on an MCP server): MCP Server id or slug. It can be found in MCP server settings. This is only valid with the deploy command (default command).\n  \n\n\nContributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/bump-sh/github-action. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.\n\nLicense\n\nThe scripts and documentation in this project are released under the MIT License.\n\nCode of Conduct\n\nEveryone interacting in the Bump.sh github-action project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct."
        },
        {
          "id": "help-continuous-integration-gitlab-ci",
          "title": "Gitlab CI",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/continuous-integration/gitlab-ci/",
          "content": "Bump.sh continuous integration examples\n  Generate your API documentation &amp; changelog with Gitlab CI\n\n\nBump.sh continuous integration examples\n\nBump.sh offers a lot of flexibility to let you configure how it should interact with your CI pipelines.\n\nAll our generic build processes are stored in a dedicated public repository called bump-ci-example. Those processes are defined thanks to dedicated configuration files and scripts which uses the Bump.sh CLI under the hood.\n\nFor more advanced needs, consider using our API.\n\nGenerate your API documentation &amp; changelog with Gitlab CI\n\nThe provided example has is a two stage delivery process:\n\n  The test stage will run an API definition diff on all your branches except the main one, to compare the changed document file with your Bump.sh documentation. It will also comment with the diff content on the related Merge Request on Gitlab if it exists.\n  The deploy stage will run only on your main branch, once your API definition was merged to your main branch it will automatically be deployed to your Bump.sh documentation.\n\n\nCopy-paste the following files in your own project to see start your first Bump related pipeline:\n\n  the .gitlab-ci.yml pipeline file\n  the whole .gitlab/ directory.\n\n\n\n  If you encounter an issue or have suggestions on those examples, please do file a ticket on the dedicated repo we would love to hear your feedback."
        },
        {
          "id": "help-continuous-integration",
          "title": "Integrate Bump.sh with your workflow",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/continuous-integration/",
          "content": "Available tools\n  Steps to integrate in your CI    \n      API diff &amp; validation of the documentation file\n      Deploy your API document\n    \n  \n  Examples    \n      Github Action\n      Other Continuous Integration tools (CI)\n      CLI\n    \n  \n  Recommendation\n\n\nHow to integrate your documentation deployment to your Continuous Integration (CI) workflow.\n\nAvailable tools\n\nThere are multiple ways to integrate with Bump.sh:\n\n\n  Using the Github Action\n  Using Azure DevOps\n  Using Circle CI\n  Using Gitlab CI\n  Using Travis CI\n  Manually using the CLI\n  More advanced usage can be done with the API\n\n\nSteps to integrate in your CI\n\nWe are presenting the process recommended to our users, but feel free to adapt it to your own workflow/requirements.\n\nWe advise to setup two steps in your automation flow:\n\n  a validation and diff step during development\n  followed by a deployment step on production merges.\n\n\n\n  You can use the CI to interact with your MCP server,\nsee examples with the CLI\nor the GitHub Action.\n\n\nAPI diff &amp; validation of the documentation file\n\nWhen suggesting a change to your API, you probably follow a pull request flow (also known as merge request) and make the changes on a development branch. You can integrate Bump.sh at this stage to generate an API diff or only validate your changed API document.\n\nWith our Github Action, you can receive automatic API diff comments directly on your GitHub pull requests. With other source code management systems, you can use our CLI within your CI with the bump diff command to run each time a development branch is created or updated.\n\nThis step will fail the build if the documentation file is not valid. You can also ask our tools to fail if a breaking change is detected on your API (Thanks to the fail_on_breaking: Github Action input parameter or --fail-on-breaking CLI option).\n\nDeploy your API document\n\nOnce your branch has been merged into your main branch (generally the master or main one) you will want to deploy your new documentation file and make it live.\n\nExamples\n\nGithub Action\n\nThe GitHub action example uses a dedicated action we crafted especially for you. You may find more information for both steps described above on our dedicated GitHub Action page.\n\n\n  Recommended: Deploy Documentation &amp; Diff on Pull Requests\n  API diff &amp; validation step\n  Deploy to your documentation\n\n\nThe GitHub Action can be used to interact with your MCP server:\n\n\n  Deploy a workflow document for your MCP server.\n\n\nOther Continuous Integration tools (CI)\n\nThe CI examples are here to help you build a similar process described with our GitHub action. We try to keep some specially crafted scripts for you to build the same experience for your own tools:\n\n\n  Example Gitlab CI pipelines.\n  Example delivery process with a CircleCI config file.\n  Example Travis CI Build Configuration\n\n\nCLI\n\nThe CLI can be used in your custom CI scripts with the two available recommendeded steps:\n\n\n  bump diff to check the changes &amp; validate the API document\n  bump deploy to publish to your Bump.sh documentation, or MCP server\n\n\nRecommendation\n\nNote that if you don’t want to keep the private token and documentation id in your code base, you should use environment variables. Our CLI and Github-Action automatically recognizes these 3 variables:\n\n\n  BUMP_ID: your documentation public slug or id\n  BUMP_TOKEN: your documentation private token\n  BUMP_HUB_ID: if using hubs, your hub public slug or id"
        },
        {
          "id": "help-continuous-integration-travis-ci",
          "title": "Travis CI",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/continuous-integration/travis-ci/",
          "content": "Bump.sh continuous integration examples\n  Generate your API documentation with Travis CI\n\n\nBump.sh continuous integration examples\n\nBump.sh offers a lot of flexibility to let you configure how it should interact with your CI pipelines.\n\nAll our generic build processes are stored in a dedicated public repository called bump-ci-example. Those processes are defined thanks to dedicated configuration files and scripts which uses the Bump.sh CLI under the hood.\n\nFor more advanced needs, consider using our API.\n\nGenerate your API documentation with Travis CI\n\nThis Travis CI example will help to validate your definition file on pull requests, and then deploy a definition file change merged in your main branch.\n\nCopy-paste the following file in your own project to see the builds running on Travis:\n\n  .travis.yml file example\n\n\n\n  If you encounter an issue or have suggestions on those examples, please do file a ticket on the dedicated repo we would love to hear your feedback."
        },
        {
          "id": "help-customization-options-color-logo-meta-images",
          "title": "Branding customization",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/customization-options/color-logo-meta-images/",
          "content": "Color\n  Logo and favicon\n  Dark mode\n  Social media image    \n      Use the automated image generation\n      Add your own image\n    \n  \n  Advanced customization through CSS variables\n  Hub customization\n\n\nColor\n\nFrom the “Customize UI” tab in your documentation settings, you can select a color scheme to apply to it, better reflecting your brand.\n\nA preview allows you to anticipate the readability of the final rendering.\n\n\n\nLogo and favicon\n\nYou can upload your own logo and favicon on Bump.sh to customize your documentation with your brand or project colors.\n\nFrom the “Customize UI” tab in the documentation settings, you can add your own logo and favicon.\n\n\n\nTo provide the best image quality, it is recommended to adhere to the following dimensions for your logo: 140px*36px. Other dimensions will be adjusted by our renderer.\n\nDark mode\n\nBy default, we apply filters to make your light mode color and logo blend in dark mode. To ensure brand consistency, you can define a custom logo and color dedicated to dark mode.\n\n\n\nSocial media image\n\nA social media image, also known as “meta image”, typically refers to an image that is used as a preview image when a web page is shared on social media platforms and can be displayed in search engine results.\nSocial media images help make the links posted on social media platforms more attractive, especially when shared or displayed in posts.\n\nBy default, Bump.sh will automatically generate a social media image that can be changed to your own later on.\n\nUse the automated image generation\n\nIn case you don’t have a visual you could use, Bump.sh will automatically generate a meta image that will display your documentation name, logo and API type.\nFirst generation of this new social media image may take a bit, but any modification or change will start the generation process.\n\n\n\n\n  The social media image will be modified each time you change the name or the logo.\n\n\nAdd your own image\n\nFrom the “Customize UI” tab in the documentation settings, you can add your own social media image.\n\nIt is advisable not to exceed a width of 1200px and to maintain the height between 600px and 700px for better integration on most platforms.\n\n\n\nAdvanced customization through CSS variables\n\n\n  Deep customization options are available in our custom plans.\n\n\nBump.sh offers deeper customization options, offering full control of your branding. It makes your Bump.sh doc portal truly blend in with your platform.\n\nCustom CSS can be added either through a reverse proxy you’ve set up (by injecting these variables in the head section of documentation), or managed by Bump.sh. Feel free to contact us so we can assist you.\n\n\n  If you customize fonts, make sure to use a font that is available on the user’s device, or loaded in your custom headers.\n\n\nYou can safely customize the style of your doc portal using these CSS variables:\n\n\n  --font-family: the global font family;\n  --heading-font-family: the font family used for headings (h1, h2, h3, …). If none is defined, the global font family is displayed;\n  --code-font-family: the font used for code blocks and examples;\n  --doc-font-size: the global font size. The default value is 14px;\n  --code-font-size: the font size used on code blocks and examples. The default value is 12px;\n  --nav-font-size:  the font size used on side and top navigation bars. The default value is 14px;\n  --doc-font-weight: the default font weight. The default value is 500;\n  --nav-font-weight: the font weight used on side and top navigation bars. The default value is 500;\n  --logo-width: logo width, if the default width doesn’t fit your logo width;\n  --logo-height: logo height, if the default height doesn’t fit your logo height;\n  --doc-success-color: the color used for success messages;\n  --doc-error-color: the color used for error messages;\n  --doc-warning-color: the color used for warning messages;\n  --doc-warning-light-color: the color used for warning messages with a lighter background;\n\n\nYou could also apply your own custom CSS, but keep in mind:\n\n\n  We don’t guarantee the stability of HTML or CSS class names\n  Custom overrides might break if we update the rendering engine\n\n\nThe Embed mode is an even more advanced way to integrate Bump.sh with your existing doc sites and branding.\n\nWe are always open to adding more customization options: if you need to customize other parts of the UI, just send us a message and we’ll add the required CSS variables.\n\nHub customization\n\nAll of these customization settings can be applied to all the documentation within a hub.\n\nThese options are available directly in the “Customize UI” tab in the hub settings."
        },
        {
          "id": "help-customization-options-custom-domains",
          "title": "Custom domains",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/customization-options/custom-domains/",
          "content": "Setting a CNAME record\n  Setting a custom domain in Bump.sh\n\n\nYour API documentation is hosted on Bump.sh under our domain name. By default, accessing your documentation will be possible via a URL such as https://bump.sh/&lt;organization_slug&gt;/doc/&lt;doc_slug&gt;.\n\nTo extend your brand experience to visitors and API consumers, you can use your own domain to replace our default URL by setting up a specific CNAME record.\n\nSetting a CNAME record\n\nFirst, you’ll need to create a CNAME record pointing to custom.bump.sh, at your domain name provider.\n\nFor instance, if you want to host your documentation under the developers.example.com domain, you will create the following record:\n\ndevelopers.example.com. 3600 IN CNAME custom.bump.sh.\n\n\n\n  The configuration of the CNAME record can vary from one hosting provider to another. Feel free to contact your hosting provider if you have any questions at this stage.\n\n\nOnce this is done, you can set your custom domain in Bump.sh.\n\nSetting a custom domain in Bump.sh\n\nFrom the settings of your documentation, check the “Custom domain” box. You will then be asked to enter the URL of your custom domain, which has been configured on your end in the previous step. Confirm by selecting “Update General Settings”. SSL certificates are automatically issued at this step.\n\n\n\n\n  It may take some time for your custom domain update to propagate. This delay is normal and is not dependent on Bump.sh or your hosting provider.\nDuring this period, visitors returning to access your documentation may have difficulty accessing it. It is recommended to try again later or clear your cache before making another attempt."
        },
        {
          "id": "help-customization-options-embed-mode",
          "title": "Embedding Bump.sh in your own portal",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/customization-options/embed-mode/",
          "content": "What is Embed Mode?    \n      Benefits\n    \n  \n  How it works    \n      Step-by-step flow\n    \n  \n  Integration Guide    \n      Configure your proxy\n      Customize the experience\n    \n  \n  Technical requirements\n  Best Practices    \n      SEO, bots &amp; metadata\n      Automatic translation\n    \n  \n  FAQ\n\n\nIf you want to offer a fully branded developer experience, Embed Mode is for you.\nIt lets you serve your Bump.sh documentation under your own domain, within your own portal, with your own layout, headers, footers, and custom scripts.\n\nThis guide walks you through how it works, how to set it up, and how to make it yours.\n\nWhat is Embed Mode?\n\nEmbed Mode lets you integrate Bump.sh-generated documentation inside your own frontend stack, giving you 100% control over your branding and navigation.\n\nIt’s designed for larger teams and companies who need their docs to look and feel like a native part of their platform or developer experience.\n\n\n  The Embed mode is available in our custom plans. If your current plan doesn’t include access, feel free to contact us so we can assist you.\n\n\nBenefits\n\n\n  Looks and feels like your own docs site\n  Keep control over headers, navigation, footers, and scripts\n  Works with your existing proxy/CDN setup\n  Bump.sh handles rendering, versioning, and hosting while you stay in control of the experience\n\n\nHow it works\n\nHere’s a high-level view of how requests and rendering flow in Embed Mode:\n\n\n\nStep-by-step flow\n\n\n  A user visits a URL like https://yourdomain.com/docs/...\n  Your proxy intercepts the request and rewrites it to point to the equivalent Bump.sh documentation URL\n  The proxy adds two headers:\n    \n      X-BUMP-SH-PROXY: YOUR_SECRET: a secret token to activate the proxy mode\n      X-BUMP-SH-EMBED: true: activate the embed mode\n    \n  \n  Bump.sh renders the documentation page including the content (intro, topic, group, operation…), menus, and metadata\n  The proxy injects your custom assets (headers, footers, scripts, etc.) into the response\n  The result is a fully integrated documentation page that feels native to your portal\n\n\nIntegration Guide\n\nConfigure your proxy\n\nTo use Embed Mode, you’ll need a reverse proxy in front of Bump.sh that can rewrite and forward traffic correctly.\n\n\n  Rewrite URLs from your domain to your public Bump.sh documentation URLs\n  Make sure the required headers (X-BUMP-SH-PROXY and X-BUMP-SH-EMBED) are added to each proxied request\n\n\nBump.sh will start rendering in Embed mode: it will change the layout and scrolling mechanism to adapt to your header, and will inject 3 customization zones in HTML that can be safely manipulated.\n\nCustomize the experience\n\nYou can inject up to three types of content into the page: head, topbar and footer.\n\nHead injection\n\nInsert content into the &lt;head&gt; section: stylesheets, metadata, scripts. This is were you can apply your branding by specifying CSS variables, load your own fonts and Javascript, and add custom scripts.\n\n&lt;!-- Bump.sh: head start --&gt;\n&lt;meta name=\"custom-head\" /&gt;\n&lt;!-- Bump.sh: head end --&gt;\n\n\nTopbar injection\n\nAdd a custom top bar, including your logo, main website links, search engine, language switcher, auth links, etc… This topbar is automatically hidden when the user scrolls down to keep most of the content visible.\n\n&lt;!-- Bump.sh: top-body start --&gt;\n&lt;div id=\"embed-top-body\"&gt;&lt;/div&gt;\n&lt;!-- Bump.sh: top-body end --&gt;\n\nFooter injection\n\nShow a branded footer with support links or legal information.\n\n&lt;!-- Bump.sh: bottom-body start --&gt;\n&lt;div id=\"embed-bottom-body\"&gt;&lt;/div&gt;\n&lt;!-- Bump.sh: bottom-body end --&gt;\n\n\nCustom CSS\n\nYou can safely style your documentation using the following CSS variables:\n\n\n  --font-family: the global font family;\n  --heading-font-family: the font family used for headings (h1, h2, h3, …). If none is defined, the --font-family is displayed;\n  --code-font-family: the font used for code blocks and examples;\n  --doc-font-size: the global font size. The default value is 14px;\n  --code-font-size: the font size used on code blocks and examples. The default value is 12px;\n  --nav-font-size:  the font size used on side and top navigation bars. The default value is 14px;\n  --doc-font-weight: the default font weight. The default value is 500;\n  --nav-font-weight: the font weight used on side and top navigation bars. The default value is 500;\n  --logo-width: logo width, if the default width doesn’t fit your logo width;\n  --logo-height: logo height, if the default height doesn’t fit your logo height;\n  --doc-success-color: the color used for success messages;\n  --doc-error-color: the color used for error messages;\n  --doc-warning-color: the color used for warning messages;\n  --doc-warning-light-color: the color used for warning messages with a lighter background;\n\n\nYou may also override styles using your own custom CSS, but keep in mind:\n\n\n  We don’t guarantee the stability of HTML or CSS class names\n  Custom overrides might break if we update the rendering engine\n\n\nWe are always open to adding more customization options: if you need to customize other parts of the UI, just send us a message and we’ll add the required CSS variables.\n\nTechnical requirements\n\nTo use Embed Mode, you’ll need a reverse proxy or CDN that allows:\n\n\n  URL rewriting\n  HTTP header injection\n\n\nHere is a list of typical platforms that support this:\n\n\n  Fastly\n  Cloudflare\n  Amazon CloudFront\n  Netlify Edge Functions\n  …\n\n\nIf you do not find the tool you are using in this list, ask us directly, and we will tell you if your platform is compatible.\n\nBest Practices\n\nSEO, bots &amp; metadata\n\n\n  Since Bump.sh handles server-side rendering, metadata and titles are preserved\n  Make sure your proxy does not block search engines if you want pages indexed\n  Ideally, your proxy identifies legit bots and transmit the information to Bump.sh infrastructure using a custom header\n\n\nAutomatic translation\n\n\n  Embed Mode works natively well with localization platforms like Smartling\n  Use your proxy to inject the correct language context and route the content through the tool you’re using\n\n\n\n  Be careful not to auto-translate reserved words (like string, boolean, etc.)\n\n\nFAQ\n\nWhat happens if the headers are missing?\n\nThe documentation won’t render in embedded mode. The default Bump.sh layout will be shown.\n\nCan I use my own JS framework on top?\n\nYes. Just make sure your injected scripts don’t interfere with the core rendering.\n\nCan I track page views with my analytics tools?\n\nYes. Just inject your analytics script via the head."
        },
        {
          "id": "help-customization-options-head-injection-analytics-tools-integration",
          "title": "Head injection (analytics and tools integration)",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/customization-options/head-injection-analytics-tools-integration/",
          "content": "How-to\n\n\n&lt;head&gt; injection allows you to add the same set of tools that you have on your own platform, for example to:\n\n  Add analytics tags (Google, Fathom, …),\n  Integrate support tools (such as Intercom or AI assistants).\n\n\n\n  Head injections are available in our custom plans.\n\n\nHow-to\n\nHead injections can be done either through a reverse proxy you’ve set up or managed by Bump.sh. Feel free to contact us so we can assist you.\n\nThe &lt;head&gt; section is the place to do it. It supports any attribute that can be added in a &lt;head&gt; section: metadata, scripts, stylesheets, fonts, …\n\n&lt;!-- Bump.sh: head start --&gt;\n&lt;meta name=\"custom-head\" /&gt;\n&lt;!-- Bump.sh: head end --&gt;"
        },
        {
          "id": "help-customization-options",
          "title": "Customization options",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/customization-options/",
          "content": "Bump.sh offers several customization options for your documentation and hubs to provide a tailored and unique experience for your API consumers.\n\nCustom Domains allow you to replace Bump.sh’s domain in URLs with your own, making it easier to access your documentation. You can also customize the color, logo and social media image to maintain your brand’s visibility.\n\nFinally, you can define the method for sorting API operations to offer a comfortable and smooth reading experience.\n\n\n  Some customization options, such as Embed mode advanced style customization, or head injection (for analytics and tools integration) depend on the plan you have subscribed to. Feel free to contact us if you’d like to take advantage of a specific feature."
        },
        {
          "id": "help-customization-options-login",
          "title": "Login page",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/customization-options/login/",
          "content": "Organizations using one or more SSO connections for access to their documentation and hubs can customize the SSO login page.\n\nIt is possible to display their logo but also to customize the connection buttons and their description (adding text to them).\nThese customization options are available upon request."
        },
        {
          "id": "help-customization-options-operations-navigation",
          "title": "Operations and navigation",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/customization-options/operations-navigation/",
          "content": "Automatic\n  Group operations by path\n  Group operations by tag\n\n\nBump.sh offers several methods for sorting your API operations to provide a smooth and coherent reading structure based on tags or operation names and URLs.\n\nFrom the “Customize UI” tab in your documentation settings, you can select a custom operations &amp; navigation grouping mode between either “Automatic”, “By Path” or “By Tag”.\n\nAutomatic\n\nBy default, Bump.sh analyzes your API definition and suggests the most suitable sorting mode, either by paths or by tags.\n\nIf first-level field tags are present at the root of your OpenAPI document object, Bump.sh will use Group by tags as a default documentation generation behaviour.\n\n\n\nGroup operations by path\n\nWhen Group operations by path is chosen, Bump.sh deduces group names from related paths. The first part of the path is extracted to generate the group name, and every operation related to it will be grouped together.\n\n\n\nGroup operations by tag\n\nWhen group operation by tag is selected, Bump.sh will use the tags from your API definition to group, name and sort operations.\n\nTags offer you better customization of your documentation, going beyond the resource names. You can also add a description for each tag, which will appear in the header of your documentation.\n\n\n  When “Group operations by tag” is selected, operations without tags will be ignored and won’t be displayed."
        },
        {
          "id": "help-documentation-experience-api-explorer",
          "title": "API Explorer",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/documentation-experience/api-explorer/",
          "content": "What is the API Explorer?\n  How to activate the API Explorer?\n  How to use the API Explorer?    \n      Sharing\n    \n  \n  Security    \n      Proxy\n      Authentication        \n          Details about OAuth2 flows\n        \n      \n    \n  \n  Server variables    \n      Example usage\n    \n  \n\n\n\n  The API Explorer is available from our Pro plan.\n\n\nWhat is the API Explorer?\n\nThe API Explorer allows you to test an API in real-world conditions directly from its documentation. It facilitates API discovery by enabling you to try out specific API calls on any server listed in your definition file. It helps you get started quickly without a testing environment or share a specific use case for discovery, support, or debugging purposes.\n\nHow to activate the API Explorer?\n\nThe API Explorer can be activated for a documentation or a whole hub through an option in its “Settings” tab.\n\n\n\nHow to use the API Explorer?\n\nThe API Explorer is accessible at any time via a button at the top of your documentation (and remains visible as you scroll). Each operation also features a button that opens the API Explorer for that specific operation.\n\n\n\nIf you haven’t opened the API Explorer from a specific operation, you can select one from the corresponding menu.\n\n\n\nWe identify the required fields directly from the definition file, making it easier for you to fill out the request, detecting whether it’s a boolean, date, etc. Fill in the expected information to execute the request and receive a response.\n\n\n\nSharing\n\nIt’s possible to share a request setup, which is useful for showing an example in a demo or for support purposes. The share button generates a URL that includes the pre-filled request parameters.\n\n\n  This URL will never share your authentication parameters or the response.\n\n\n\n\nIf the API documentation has been updated after the share URL was generated, the Explorer will notify you that the pre-filled fields may have changed and display a link to the API changelog for a more detailed review of the changes.\n\n\n  The curl commands generated when filling out the form are copyable.\n\n\nSecurity\n\n\n  To ensure maximum security and confidentiality of requests, Bump.sh does not collect or store any confidential data during API Explorer use.\n\n\nProxy\n\nTo ensure optimal compatibility and total confidentiality, we chose to let the user’s browser process the requests rather than our main servers. Despite the obvious advantages, requests sent from a browser are sometimes poorly received by servers and result in what are known as CORS errors.\n\nTo avoid this situation, we created our own proxy, designed to ensure that requests sent from a browser are processed without error by an API’s servers, all while maintaining confidentiality and security: cors toujours.\n\nThis proxy is hosted outside our infrastructure to ensure data security. Its open-source code is available on GitHub.\n\nYou can also learn more about it in our dedicated blog post.\n\nA future update will add an option to disable the proxy (meaning you’ll have to properly set up your CORS settings to use the API Explorer if you disable it).\n\nAuthentication\n\nWe support authentication for APIs that require prior authentication. Three options are available: via HTTP authorization (Basic or Bearer tokens), via API key (In header, query param or cookie) or via OAuth2 flows. These are the most common security schemes available in OpenAPI and AsyncAPI.\n\nThe authentication information are stored in the localStorage of the user’s browser, to avoid having to fill it out on every refresh/operation switch.\n\n\n\n\n  When sharing a request, this authentication information is never transmitted.\n\n\nDetails about OAuth2 flows\n\nFor now we partially support the “Implicit” OAuth2 grant type for automatic access token retrieval. To enable this feature you will need to include an x-client-id vendor extension in the API definition file, in the OAuth2 implicit flow object. This value should be the ID of a dedicated OAuth client application created on your Authorization server to identify your API consumers.\n\nE.g. for example:\ncomponents:\n  securitySchemes:\n    \"OAuth2 implicit flow\":\n      type: oauth2\n      flows:\n        implicit:\n          authorizationUrl: \"https://auth.example.org/oauth\"\n          scopes: {}\n          x-client-id: \"123456abcdef\"\n\n\n\n\nAll other OAuth2 flows are partially supported and will display an input field for the user to enter an access token manually (similarly to the HTTP authorization security scheme).\n\n\n\nServer variables\n\nWe support server variables in the API Explorer. Servers defined in the OpenAPI definition with variables in their paths are translated into enums and strings to be filled by the user of the API Explorer. It can be useful, for example, to choose between multiple server regions or to send requests to a subdomain that is specific to each API user.\n\nA default value can be set for each variable. If no value has been defined, the variable field is filled with the variable’s name. Variable descriptions are displayed as tooltips above variables to guide the user into filling fields with the right information.\n\nThe server variables are stored in the localStorage of the user’s browser, to avoid having to fill it out on every refresh/operation switch.\n\n\n  If the domain is not clearly defined in the definition (for example by having https://{my-variable} instead of https://{my-variable}.my-domain.com), our proxy will block the request. More details about why we use a proxy here.\n\n\nLearn more about server variables in our OpenAPI guide.\n\nExample usage\n\nBy defining two variables, region and docId, in the OpenAPI definition:\nservers:\n  - url: https://{region}.bump.sh/{docId}\n    description: The production API server\n    variables:\n      docId:\n        description: Can be found in your documentation settings.\n      region:\n        enum:\n          - \"east-eu\"\n          - \"west-eu\"\n        default: \"east-eu\"\n\n\nThe API Explorer renders a dynamic path with a select for the region, and an input for the docId:"
        },
        {
          "id": "help-documentation-experience-ask-ai-markdown-rendering",
          "title": "Ask AI & Markdown rendering",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/documentation-experience/ask-ai-markdown-rendering/",
          "content": "Ask AI\n  Markdown rendering\n\n\nMarkdown version of your API docs and hubs, optimized for AI tools, are offered next to your standard docs. These Markdown versions, by providing narrowed-down contexts, reduce token cost and hallucinations, therefore returning more relevant results.\n\nAsk AI\nMany API consumers now rely on AI tools to help them quickly discover API capabilities. The Ask AI dropdown menu offers documentation users easy access to ChatGPT and Claude, alongside a one-click Markdown access.\n\n\n\nMarkdown rendering\nTo access the Markdown version of a documentation, add .md at the end of the URL (or /source.md if you have a custom domain, when the URL is the root of your documentation).\n\nOn a documentation root (example truncated for visibility purposes).\n# Bump.sh Api\n\n## Description\nThis is version `1.0` of this API documentation. Last update on Jun 16, 2025.\nThis is the official Bump.sh API documentation. Obviously created with Bump.sh.\n\nThe Bump.sh API is a REST API. It enables you to [...]\n\n\n## Servers\n- https://bump.sh/api/v1: https://bump.sh/api/v1 ()\n\n\n## Endpoints\n- [Branches](https://developers.bump.sh/group/endpoint-branches.md)\n- [Diffs](https://developers.bump.sh/group/endpoint-diffs.md)\n- [...]\n\n\n## Webhooks\n- [Documentation change](https://developers.bump.sh/group/webhook-documentation-change.md)\n\n\nFor a specific operation (example truncated for visibility purposes).\n# Create a new version\n\n**POST /versions**\n\nDeploy a new version for a given documentation, which will become the current version.\n\n\n## Servers\n- https://bump.sh/api/v1: https://bump.sh/api/v1 ()\n\n\n## Authentication methods\n- Authorization token\n- Basic token\n\n\n## Body parameters\nContent-type: application/json\nThe version creation request object\n- **documentation** (string)\n  UUID or slug of the documentation.\n- [...]\n\n## Responses\n### 201: Documentation version successfully created\n\n#### Body Parameters: application/json (object)\n- **id** (string)\n  Unique id of your version.\n- [...]"
        },
        {
          "id": "help-documentation-experience",
          "title": "Enhance your documentation content",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/documentation-experience/",
          "content": "API documentation deserves a bit more writing. It can be useful to add an introduction or a quick tutorial. You might want to provide context, additional reading or understanding materials.\n\nThe API Explorer allows you to test an API in real-world conditions directly from its documentation, without the need to set up a test environment on your side. It also enables you to quickly share a request for discovery, support, or debugging purposes.\n\nTo help you enrich your API documentation, Bump.sh supports Markdown, offering better formatting and the ability to add your own images.\n\nWe also offer topics: an exclusive custom property that allows you to add extra information to your documentation."
        },
        {
          "id": "help-documentation-experience-markdown-support",
          "title": "Markdown support",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/documentation-experience/markdown-support/",
          "content": "Common Markdown syntax support    \n      Titles &amp; headings\n    \n  \n  Multi-line code blocks with language color syntax highlighting    \n      Information call-outs\n      Accordions\n    \n  \n  Diagrams and charts (Mermaid Support)\n  Images    \n      Image sizing\n    \n  \n  Markdown files as an external reference\n\n\nThe generation of your API documentation is based on your API definition, which must adhere to one of the standards we support (OpenAPI/Swagger and AsyncAPI.).\n\nTo allow you to enrich your documentation, we support common Markdown syntax, including call-outs and the addition of images, for example.\n\nYou can integrate Markdown directly into your API document or as an external reference pointing to Markdown files.\n\nCommon Markdown syntax support\n\n\n  \n    \n      Formatting\n      Markdown Syntax\n      Rendering\n    \n  \n  \n    \n      bold\n      **bold**\n      bold\n    \n    \n      italic\n      _italic_\n      italic\n    \n    \n      link\n      [links](https://bump.sh)\n      links\n    \n    \n      inline code\n      ̀ inline code ̀\n      inline code\n    \n    \n      highlight\n      ==highlight==\n      highlight\n    \n    \n      strike-through\n      ~~strikethrough~~\n      strikethrough\n    \n    \n      footnote\n      Footnote[^1]\n      Footnote[^1]\n    \n    \n      quotes\n      &gt; quotes\n      &gt; quotes\n    \n  \n\n\nTitles &amp; headings\n\n\n  Heading 1: # A first-level title\n  Heading 2: ## A second-level title\n  Heading 3: ### A third-level title\n\n\nMulti-line code blocks with language color syntax highlighting\n\nE.g.\n\n    ```json\n    {\n      \"hello\": \"world\",\n      \"number\": 1,\n      \"boolean\": true\n    }\n    ```\n\n\nwill render:\n\n{\n  \"hello\": \"world\",\n  \"number\": 1,\n  \"boolean\": true\n}\n\n\nInformation call-outs\n\nBump.sh supports information call-outs (of type info, warn, success or error) with the quote markdown syntax (lines starting with &gt;  ) if the first line contains one of the call-out types.\n\nE.g.\n\n&gt; info\n&gt; this is an important information to **standout**.\n\n\nwill render:\n\n\n  this is an important information to standout.\n\n\nAccordions\n\nBump.sh support of accordions offers you a way to hide large content by default, for example if you need to share large tables.\n\nE.g.\n\n&lt;details&gt;&lt;summary&gt;My accordion title&lt;/summary&gt;\nMy content\n&lt;/details&gt;\n\n\nwill render:\n\n\n\nDiagrams and charts (Mermaid Support)\n\nThe direct integration of Mermaid via our Markdown support allows you to display diagrams and charts to visually illustrate key information, making it easier to understand. You can find the full list of diagrams supported by Mermaid on this page.\n\nTo create your diagram or chart, we recommend referring to Mermaid’s syntax guide, available in their documentation. It provides detailed syntax instructions for each type.\n\nTo generate one, add a code block declared as mermaid inside a description or content property:\n\n  get:\n    description: |\n      ```mermaid\n      erDiagram\n        CUSTOMER }|..|{ DELIVERY-ADDRESS : has\n        CUSTOMER ||--o{ ORDER : places\n        CUSTOMER ||--o{ INVOICE : \"liable for\"\n        DELIVERY-ADDRESS ||--o{ ORDER : receives\n        INVOICE ||--|{ ORDER : covers\n        ORDER ||--|{ ORDER-ITEM : includes\n        PRODUCT-CATEGORY ||--|{ PRODUCT : contains\n        PRODUCT ||--o{ ORDER-ITEM : \"ordered in\"\n      ```\n\nThis example would render the following diagram (see it live):\n\n\n\nImages\n\nUse the following syntax to add images in your markdown:\n![Alt text](/path/to/image.jpg)\n\n\nPlease, don’t forget to add an alt-text to your images: this description helps make them accessible to all your readers.\n\nImage sizing\n\nIf you want to manually set the size of your image you can use our custom =dimension parameter just before the closing parenthesis as:\n\n![Alt text](/path/to/image.jpg &quot;Image title&quot; =dimension)\n\n\n=dimension uses the following syntax:\n=[width][unit]x[height][unit]\n\n\nfor instance using =100pxx50px where\n\n  100 is the width\n  px the unit\n  x the separator\n  50 the height\n  second px is unit again\nwill output an image with 100 pixels height and 50 pixels width.\n\n\nAt least one height or width parameter is mandatory, everything else being optional.\n\n=100pxx50px   # with everything\n=100x50       # without unit\n=100          # without height (x separator not needed) and unit\n=100px        # without height\n=x50          # without width and unit\n=x50px        # without width\n\n\n\n  \n    If you don’t specify a unit it will default to pixel.\n    If you don’t specify width or height, the other value will be a ratio calculated from the original size of the image so it doesn’t shrink.\n  \n\n\nYou can use any of the following CSS length units as unit:\n\nAbsolute units:\n\n  cm centimeters\n  mm millimeters\n  in inches (1in = 96px = 2.54cm)\n  px pixels (1px = 1/96th of 1in)\n  pt points (1pt = 1/72 of 1in)\n  pc picas (1pc = 12 pt)\n\n\nRelative units:\n\n  em relative to the font-size of the element (usually 1em = 16px)\n  ex relative to the x-height of the current font (rarely used)\n  ch relative to the width of the “0” (Unicode U +0030) in the current font\n  rem relative to font-size of the root element\n  vw relative to 1% of the width of the viewport*\n  vh relative to 1% of the height of the viewport*\n  vmin relative to 1% of viewport’s* smaller dimension\n  vmax relative to 1% of viewport’s* larger dimension\n  % relative to the parent element\n\n\nMarkdown files as an external reference\n\nMarkdown files can be included as an external reference within your API definition with the $ref syntax $ref: \"./path/to/local-markdown.md\".\n\nIn the same way you can extract part of your API definition (usually JSON schema of your models into dedicated *.yaml or *.json files), you can extract your markdown content into dedicated files too.\n\nE.g. Your OpenAPI file api-contract.yml can thus looks like:\n\nopenapi: 3.1.0\ninfo:\n  title: Bump API documentation\n  version: 1.0.0\n  description:\n    $ref: \"./docs/introduction.md\"\nservers:\n  ...\npaths:\n  ...\n\n\nThese files will be rendered within your documentation as if they were part of your API definition."
        },
        {
          "id": "help-documentation-experience-topics",
          "title": "Topics",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/documentation-experience/topics/",
          "content": "Use x-topics\n  External references\n  Public documentation examples\n\n\nx-topics is a vendor-specific property that we’ve created to allow you to add extended content to your API documentation. They enable you to add context, additional information, or even tutorial elements to guide your readers in using your API.\n\nUse x-topics\n\n\n  \n    \n      Property\n      Description\n    \n  \n  \n    \n      title\n      Topic title as it will appear in the navigation bar and in the content section.\n    \n    \n      content\n      The topic content. Markdown is fully supported here.\n    \n    \n      example\n      Will appear in the examples section, if activated. Markdown is fully supported here.\n    \n  \n\n\nExample:\n\nx-topics:\n  - title: Getting started\n    content: Before using the API you need to get an API key by sending us an email.\n  - title: Authentication\n    content: Send the `X-API-KEY` header with all your requests.\n    example: |\n      ```\n      $ curl \\\n        -X POST https://api.example.com/endpoint/ \\\n        -H \"X-API-KEY: XXXXXX\" \\\n      ```\n\n\nExternal references\n\nYou can write your topics in dedicated Markdown files to keep your OpenAPI definition file clean. To reference a Markdown file, add a $ref link inside the content object of your topic.\n\nx-topics:\n  - title: Getting started\n    content: \n      $ref: ./getting-started.md\n  - title: Authentication\n    content:\n      $ref: ./authentication.md\n    example:\n      $ref: ./authentication-example.md\n\n\nPublic documentation examples\n\nHere are some examples of public documentation made by teams using x-topics:\n\n\n  Memo Bank API Documentation\n  Pexip Engage API Documentation"
        },
        {
          "id": "help-faq",
          "title": "FAQ",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/faq/",
          "content": "Will you support others specifications like BluePrint or GraphQL?\n\nWhen we initially had the Bump idea, we though that it would be absolutely perfect to be able to handle any kind of documentation.\n\nThis said, at the moment, we are focusing on OpenAPI and AsyncAPI to offer the best support of these specifications.\n\nWhy not supporting all the OpenAPI specification?\n\nIf you have tried Bump, you probably have already discovered that we don’t support the whole Swagger / OpenApi specification.\n\nThere are 2 reasons for that:\n\n\n  first, we are a fresh new project, which is far from being complete. So we may have decided to postpone some features due to our priorities.\n  second, we may have decided that a specification feature adds too much complexity to the documentation, and have preferred to simply ignore it to keep things simple for now.\n\n\nFeel free to ask us why a feature you need is missing, we’ll be happy to answer and try to find a solution with you.\n\nSecurity and confidentiality\n\nBump.sh has been designed from the ground up with security as a core principle. Our platform never accesses your infrastructure or source code, and only processes data explicitly sent by you.\n\nWe only handle two types of strictly controlled data:\n\n\n  API documents (OpenAPI, AsyncAPI) explicitly provided by your teams via our secure API.\n  User information (email, name, role), created automatically via SSO or manually within the tool.\n\n\nIntegration into your workflow is secure and transparent. You retain full control over what data is shared and how:\n\n\n  Direct uploads via our API through your own integration code.\n  Using our open-source CLI integrated into your CI pipeline, which exclusively sends files explicitly defined by you.\n  Utilizing our GitHub Action, also open-source, built upon the same secure, vetted codebase as our CLI.\n\n\nAdditionally, we implement industry-leading security practices, including WAF protection, continuous monitoring, daily dependency updates, regular staff security training, and authorized penetration tests conducted by our customers.\n\nAs a French company, we fully comply with GDPR, adhering to stringent data protection practices outlined in our Data Processing Agreement (DPA)."
        },
        {
          "id": "help-getting-started-concepts",
          "title": "Important concepts",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/getting-started/concepts/",
          "content": "Global concepts    \n      Specification\n      Deployment\n    \n  \n  API documentation    \n      API definition\n      API contract\n      API document\n      Release\n      API change\n      Breaking change\n      Changelog\n    \n  \n  MCP servers    \n      MCP server\n      Workflow\n      Workflow definition\n      Workflow document\n    \n  \n\n\nBefore we proceed further, we wanted to share some vocabulary to clarify what we are talking about throughout this documentation.\nThe terminology used here aims to be as accurate as possible, but you may encounter different terms elsewhere. We have chosen to use the most common and meaningful terms.\n\n\n\nGlobal concepts\n\nSpecification\n\nSpecifications are the official standards used to describe, among other things:\n\n  an API (OpenAPI, AsyncAPI, …),\n  an API workflow (Flower, Arazzo, …).\nThese standards outline a set of elements and rules to follow when writing an API definition.\n\n\nWe support OpenAPI, AsyncAPI (up to 2.6), and Flower (our internal workflow specification). Arazzo will be supported soon. To learn more about the supported specifications, we have written dedicated guides on OpenAPI and AsyncAPI. We also made a guide on Flower, our own workflow standard.\n\nDeployment\n\nA deployment is the processing of an API or a workflow document by Bump.sh: after the upload, it validates the definition against its specification, analyses its content and either:\n\n  makes it available on the MCP server, for a workflow document,\n  prepares the release, for an API document.\n\n\nAPI documentation\n\nAPI definition\n\nAn API definition is the representation of an API written in compliance with an existing specification.\nIt is sometimes referred to as an API schema, API contract, or even API specification (which is something different).\n\nAs of today, Bump.sh supports the following specifications: OpenAPI (Swagger), and AsyncAPI.\n\nWe provide a detailed introduction to API definitions through this guide.\n\nAPI contract\n\nAn API contract represents the use of an API definition when it defines an agreement, a contract between API developers, its consumers, how it is used, and the tools that surround it.\n\nAPI document\n\nAn API document refers to the file containing an API definition.\n\nRelease\n\nThe step following a successful deploy. During the release, the documentation is updated, the changelog reflects the related changes, and notifications, if configured, are sent out. The release step can be automatic, or manual.\n\nAPI change\n\nAn API change refers to a change in the structure of an API. After the release of an API definition, it is represented by a new entry in the API changelog.\n\nBreaking change\n\nBreaking changes are the ones your API consumers should not miss and are highlighted in the changelog. Here is a non-exhaustive list of criteria that help us determine if a change is breaking or not:\n\n  Renaming/Deleting an endpoint or operation\n  Renaming/Deleting a property (body, parameters, etc…)\n  Changing the “type” attribute of a property\n  Removing polymorphism of a property\n  Marking an existing property as required\n  Add or delete a security requirement\n\n\nChangelog\n\nAfter each deployment, Bump.sh updates the documentation’s changelog, listing all API changes between the current deployment and the previous one. If the “Track all changes” option is enabled, the changelog will also display changes in the documentation.\n\nMCP servers\n\nMCP server\n\nAn MCP (Model Context Protocol) server makes your API workflows available to LLM-based applications and agents. It processes API workflow documents and handles the runtime execution of these workflows. The MCP standard is supported by many AI tools: adding an MCP server is often just a matter of pasting the server URL into the “Connectors” or “Tools” section of the AI app.\n\nWorkflow\n\nA workflow is a chain of multiple API calls designed to achieve specific business goals, rather than just providing a list of endpoints.\n\nWorkflow definition\n\nA workflow definition is the representation of an API workflow written in compliance with a workflow specification like Arazzo or Flower. It describes a sequence of API operations and the goal of this sequence, their dependencies, and the data flow between them (inputs and outputs).\n\nWorkflow document\n\nA workflow document refers to the file containing a workflow definition. These documents are deployed to Bump.sh where they’re validated, processed, and made available on MCP servers for execution."
        },
        {
          "id": "help-getting-started-going-further",
          "title": "Going further",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/getting-started/going-further/",
          "content": "Access management\n  Group documentation in hubs\n  Customization options\n\n\nNow that you have a good view of what Bump.sh has to offer, you may want to go further. There are numerous options available to you to enhance your use of Bump.sh.\n\nAccess management\n\nWhether it’s for internal use within your team or for an entire community, Bump.sh offers many options to adjust access to your documentation.\n\nYou can create an organization and assign roles to members of your team and API consumers.\n\nTo go even further, you could configure access management for each of your documentation or hubs.\n\nGroup documentation in hubs\n\nYour ecosystem might include multiple APIs that also need their own documentation, and Bump.sh offers various tools to assist you.\n\nLearn how to add additional stand-alone documentation or consider creating a hub, a library of API documents.\n\nYou also have the option to choose how to upload your API documents to Bump.sh and have finer control over their release.\n\nCustomization options\n\nThere are numerous possibilities to customize your documentation and incorporate your brand.\n\nUsing a custom domain allows you to integrate your API documentation under your domain and a unified URL. You can also customize the logo or colors of your documentation to display your brand.\n\nFeel free to take a look at our dedicated help page for more information."
        },
        {
          "id": "help-getting-started",
          "title": "Quick start",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/getting-started/",
          "content": "Bump.sh connects people with REST and event-driven APIs by centralizing their documentation, highlighted with the latest changes and updates, while also powering MCP servers that execute API workflows for AI agents and tools.\n\nFrom your API definitions (OpenAPI and AsyncAPI), Bump.sh automatically generates documentation and detailed changelogs. From your workflow definitions (Flower, and Arazzo soon), it deploys MCP servers that execute real business workflows. Both offerings include extensive customization options to provide a unique experience that reflects your brand.\n\nThis setting started guide aims to get you started quickly, whether you’re working with API definitions, workflow documents, or both.\n\nYou can start by deploying your first API documentation and exploring customization options, or dive into MCP servers for workflow execution. Either path will help you get the most out of Bump.sh before digging further into advanced features."
        },
        {
          "id": "help-getting-started-start-your-first-mcp-server",
          "title": "Start your first MCP server",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/getting-started/start-your-first-mcp-server/",
          "content": "Create your Bump.sh account\n  Create your first MCP server    \n      Step 1: Create your organization\n      Step 2: Create your MCP server\n      Step 3: Use your MCP server\n    \n  \n\n\nHaving a running MCP server with Bump.sh in a matter of minutes. Create your account, upload your workflow document, and you’re good to go.\n\nCreate your Bump.sh account\n\nYou can either create your account by using your email address or directly sign up using your GitHub account.\n\n\n\nCreate your first MCP server\n\nStep 1: Create your organization\n\nOrganizations allow you to work alone or collaboratively on your MCP servers, API documentation, and hubs (collections of documentation).\n\n\n\nStep 2: Create your MCP server\n\nYou are now ready to create your MCP server. Some information are required: the most important one is the server URL. It’s the URL you’ll use to add the MCP server to your AI tool.\n\n\n\nThe last step is to upload your workflow definition. You can do it using our web upload service, but also with our multiple CI methods.\n\n\n\nStep 3: Use your MCP server\n\nAnd just like that, your first MCP server is live! You can now add it to your favorite AI tool and start using it."
        },
        {
          "id": "help-getting-started-upload-your-first-definition",
          "title": "Upload your first API definition",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/getting-started/upload-your-first-definition/",
          "content": "Create your Bump.sh account\n  Create your first documentation    \n      Step 1: Create your organization\n      Step 2: Create your documentation\n      Step 3: Enjoy\n    \n  \n\n\nPublishing your first API documentation on Bump.sh will only take you a few moments. Creating an account, documentation, and uploading your first API definition can be done in just a few clicks.\n\nCreate your Bump.sh account\n\nThe very first step is to create your Bump.sh account.\n\nTwo options are available: using your email address, or through your GitHub account.\n\n\n\nCreate your first documentation\n\nStep 1: Create your organization\n\nNow that your Bump.sh account is created, you will immediately be prompted to create your first organization.\nAn organization allows you to work alone or collaboratively on your API documentation and hubs (a collection of documentation) within the same workspace.\n\n\n\nStep 2: Create your documentation\n\nOnce the organization is created, you’re now ready to create your first documentation.\n\n\n\nThen, all that’s left is to upload your first API definition. You can also use our CLI or use one of the provided examples to get started with Bump.sh.\n\n\n\nStep 3: Enjoy\n\nAnd there you have it: you’ve just deployed your first API documentation!\nYou can already check out the result or start exploring the many options available to you for customization, inviting your team and API consumers, or creating new ones."
        },
        {
          "id": "help-hubs-create-and-manage-hubs",
          "title": "Create and manage hubs",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/hubs/create-and-manage-hubs/",
          "content": "Create a hub\n  Add documentation to a hub    \n      Using the webapp        \n          Create a new documentation\n          Transfer an existing documentation\n        \n      \n      Using the CLI\n      Using the API\n    \n  \n  Hub access management\n  Group by categories\n\n\nCreate a hub\n\nIn your dashboard, click on New hub.\n\n\n\nYou will be asked to choose a name for the hub, which will be visible to everyone. The slug is the portion of the URL that you can customize but you can also choose to use a custom domain instead:\n\n  By default, on Bump.sh, your hub will be available on this URL https://bump.sh/hub/\n  With a custom domain, your hub will be available on https://www.custom.domain.com\n\n\nFinally, you can decide whether this hub is visible to everyone or only to members of your organization.\n\n\n\nAdd documentation to a hub\n\nUsing the webapp\n\nCreate a new documentation\n\nFrom the “Hubs” tab, click on “Create Documentation” to add it to the desired hub. You can then specify the documentation’s name, its slug, and its access level (Public or private).\nDuring the creation of a stand-alone documentation, you’ll also find the option to add it automatically to an existing hub.\n\nTransfer an existing documentation\n\nFrom any documentation settings, you can choose to move it to an existing hub. The “Hubs Settings” section allows you to choose which hub to associate this documentation with. You can also specify a category if necessary.\n\n\n\nUsing the CLI\n\nThe CLI allows you to deploy new documentation directly to a hub. It does not support yet the possibility to create a hub, nor to transfer a doc from one hub to another (you will need to do that via the webapp, as described above).\n\nTo deploy a new doc to a hub, execute the following command:\n\nbump deploy your/doc.yml --auto-create --doc my-doc --hub hub-slug-or-id --token my-doc-token\n\nFind here the commands and more information about the CLI.\n\nUsing the API\n\nThe Bump.sh API allows for several actions on your hub and the documentation it contains:\n\n\n  Deploy a new version of a documentation present in a hub.\n  Validate a version (pre-deployment check) for a documentation in a hub.\n  List the documentation in a hub.\n\n\nHowever, the API does not currently support the following actions:\n\n\n  Deleting a documentation from a hub.\n  Attaching an existing documentation (outside of the hub) to a hub.\n  Modifying the metadata of a hub (title, description, settings, etc.).\n  Deleting a hub.\n\n\nFor more information, please refer to our API documentation.\n\nHub access management\n\nAccess Management functions in a similar way for hubs, with a few exceptions.\n\nThe access management settings for a hub apply to all the documentations within it.\nTwo settings are possible:\n\n\n  Private: All documentations are accessible only to members of your organization and by invitation.\n  Public: All documentations are publicly accessible, but you can manually switch them to private in their settings.\n\n\nGroup by categories\n\nCategories are visible on the homepage of your hub and allow you to organize the documentation. You can add one or more categories to them by reaching the documentation settings.\nFrom the settings, you can choose to display the documentation sorted by categories."
        },
        {
          "id": "help-hubs-customize-ui",
          "title": "Hub UI customization",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/hubs/customize-ui/",
          "content": "These hub settings are applied by default to all the documentation it contains. From the Hub Settings tab, you can customize several general options.\n\nUI design and branding\n\nIt is also possible to modify the settings of a documentation within a hub to customize it differently.\n\nYou can customize the visual appearance of your hub and its documentation by adding your logo and favicon directly from this section. By default, Bump.sh automatically generates a social media visual for you (when you share the URL of your hub or documentation on one of them). However, you can still choose your own image by uploading it at this step.\n\n\n\nYou can also customize the general color scheme of your hub.\n\n\n\nDark mode\n\nBy default, we apply filters to make your light mode color and logo blend in dark mode. To ensure brand consistency, you can define a custom logo and color dedicated to dark mode.\n\n\n\nAdvanced customization through CSS variables\nThe advanced customization settings available for documentation can also be applied to all the documentation within a hub.\n\nOperations sorting\n\nFinally, you can choose to apply a general sorting parameter to all the documentations in your hub with the Group by operations (by path or tag) and Navigation options.\n\n\n\nDocumentation listing\n\nThis setting is useful when you have multiple API docs that you want to publish in a same Hub, and consider grouping them under categories.\n\nCategories can be, for instance, groups of API that belong to a same business domain. Categories must be specified for each doc in the hub. Read how to set categories on a doc.\n\nWhen the “Group documentation by category” setting is selected, any documentation with no specified category will not be displayed in the Hub.\n\n\n\nFilter documentation selector based on categories\n\nHub categories can filter the documentation selector in the top navigation bar of a documentation, so readers only see relevant docs from the same category, improving navigation."
        },
        {
          "id": "help-hubs-hub-settings",
          "title": "General hub settings",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/hubs/hub-settings/",
          "content": "Settings    \n      Slug &amp; Custom domains\n      Description\n    \n  \n  Hub Access\n  API Explorer\n\n\nSettings\n\nSlug &amp; Custom domains\n\nThe slug directly affects the URL of your hubs and documentation. By default, it takes the name of your hub, but modifying it allows you to customize the URL.\n\nBump.sh does not automatically redirect to the new URL after changing the slug, so be sure to notify your users of this change.\n\nThe Custom Domain option allows you to keep your users within your brand experience by fully customizing the URL of your documentation.\n\n\n  Custom domain requires additional configuration on your end\n\n\nDescription\n\nThis option allows you to add a description to your Hub, and supports Markdown for enhanced text formatting. For instance, you can explain what this collection includes in terms of documentation, share important details, or even provide a contact for questions.\n\n\n\nHub Access\n\nAccess management can be configured either at the documentation level or at the Hub level.\n\n\n  If a Hub is public, all docs in that Hub are public, except the docs set to Private in the doc settings.\n  If a Hub is private, all docs in that Hub are private.\n\n\nAPI Explorer\n\nActivating the feature on a hub will activate the feature for all docs in the hub."
        },
        {
          "id": "help-hubs",
          "title": "Hubs",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/hubs/",
          "content": "What is a hub\n\n\nWhat is a hub\n\nHubs allow you to manage collections of API documentation simply and efficiently.\n\nWith hubs, your API ecosystem is displayed on a single page, where documentation can be sorted by categories.\n\nHubs also provide a unified changelog, making it easy to identify all changes across all the documentation it contains at a glance. Search is also global, allowing you to search across all documentation.\n\nNext step: Create your first hub"
        },
        {
          "id": "help",
          "title": "Bump.sh Documentation",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/",
          "content": "Global\n  API doc portal\n  MCP servers\n\n\nLearn how to get the most of Bump.sh for your API ecosystem.\n\nGlobal\n\n  Manage your Bump.sh organization – Invite teammates or partners to your ecosystem.\n  Getting started – Learn about Bump.sh, how to deploy your first specification file and explore the possibilities.\n\n\nAPI doc portal\n\n  Specification support – Which and how specifications are supported.\n  Changes management – Never miss any changes of your API, check them with the diff.\n  Hubs – Create groups of documentation.\n  Enhance your documentation content – Best ways to improve your documentation for your API consumers.\n  Customization options – Various options to customize your documentation and hubs to your needs.\n\n\nMCP servers\n\n  Flower specification – Learn how to write your workflow definition.\n  Use your MCP server – Connect your MCP server to your favorite AI tools.\n  Debug sessions – See how agents use your APIs, track errors, and optimize workflows."
        },
        {
          "id": "help-mcp-servers-access-management",
          "title": "Access management",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/mcp-servers/access-management/",
          "content": "MCP server visibility    \n      Authenticating with private MCP servers\n    \n  \n  Authenticating with APIs\n\n\nMCP server visibility\n\nYou can choose whether your MCP server is public or private:\n\n  Public means anyone can access your MCP server, given they have the server URL,\n  Private means users need to be authenticated with their Bump.sh account and be members of your organization to access your MCP server.\n\n\nServer visibility is set during the MCP server creation, and can be changed at any time in your MCP server settings.\n\n\n\nAuthenticating with private MCP servers\n\nUsers are required to authenticate before interacting with private MCP servers. This is the typical flow:\n\n  The MCP server is added to a tool: Claude, ChatGPT, Cursor, … (see our tutorials),\n  The tool reaches the MCP server and gets notified that the server is private,\n  A “connect” button appears next to the MCP server entry, or an authorization page automatically opens,\n  If the user isn’t signed in to Bump.sh yet, he has to sign in,\n  Then, if the user is an authorized member of the MCP server on Bump.sh, he gets an authorization page.\n\n\nOnce authorized, the tool gets access to workflows provided by the MCP server.\n\n\n\nAuthenticating with APIs\n\nWhile the visibility of the MCP server defines if a user has access to it, or not, your workflow APIs themselves can require authentication. \nSetting secrets in Bump.sh is the way to go."
        },
        {
          "id": "help-mcp-servers-create-and-manage-mcp-servers",
          "title": "Create and manage MCP servers",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/mcp-servers/create-and-manage-mcp-servers/",
          "content": "Where can I find my MCP servers?\n  Create an MCP server\n  Start and stop an MCP server\n  Delete an MCP server\n  MCP server states\n\n\nWhere can I find my MCP servers?\n\nYour MCP servers are listed in your dashboard, alongside your APIs and hubs. If you don’t have any MCP servers, the dedicated section will be at the bottom of your dashboard, below your API docs.\n\n\n\nCreate an MCP server\n\nTo create a new MCP server, go to your dashboard and click on the “New MCP server” button. A few essential information are required.\n\n\n  \n    \n      Server name\n      The name of the MCP server as it will appear to those who have access to it.\n    \n    \n      Server URL\n      The server URL allows you to define the URL of your MCP server, in the format https://run.bump.sh/your_organization_slug/your_server_slug/mcp. If empty, the path will be generated based on the server name.\n    \n    \n      Deployment method\n      Allows you to choose the deployment type that best suits your workflow. You can modify it at any time.\n    \n  \n\n\n\n\nThe next step is to deploy a workflow document so that the MCP server knows which workflows to expose and process. The web upload is great to quickly test your MCP server, but we have ways to integrate in your development workflow, detailed in our next section named Deploy workflows.\n\n\n\nStart and stop an MCP server\n\nYou can start and stop your MCP server in your server settings. Note that the MCP server will start automatically following your first deployment.\n\n\n\nDelete an MCP server\n\nTo delete your MCP server, go to your server settings.\n\n\n  All deletions are final. The server won’t be available anymore, and we cannot restore or recover any portion of the data once it’s deleted.\n\n\n\n\nMCP server states\n\nYou can check the state of your MCP server at any time in your dashboard or in your MCP server settings, next to its name.\n\n\n  \n    \n      State\n      Description\n    \n    \n      ⚪️ Stopped\n      Your MCP server is offline and doesn’t process any requests.\n    \n    \n      🟢 Running\n      Your MCP server is live and is processing requests based on your last workflow deployment.\n    \n    \n      🔴 Errored\n      An error on our infrastructure prevents your MCP server from running. We are working on it."
        },
        {
          "id": "help-mcp-servers-debug-sessions",
          "title": "Debug sessions",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/mcp-servers/debug-sessions/",
          "content": "Start a debug session    \n      Pass inputs\n    \n  \n  Read the response    \n      Quick check\n      Understand the trace\n      Example\n    \n  \n  Security and access\n\n\nA debug session is a temporary authorization to access execution traces on the data plane. It lets you see which API calls your MCP server actually executes, what responses it receives, and how it produces its final output. Sessions last 4 hours max and only one can be active at a time.\n\nStart a debug session\n\nGo to your MCP server settings and create a new session. You will get a unique token to authenticate your requests.\n\nFrom the debug session page, select a tool to get a pre-filled cURL command. The request uses 4 variables:\n\n\n  ORGANIZATION_SLUG: your Bump.sh organization slug.\n  MCP_SERVER_SLUG: your MCP server slug.\n  DEBUG_TOKEN: your unique session token.\n  TOOL_NAME: the tool to debug, as defined in your workflow file.\n\n\nPass inputs\n\nIf the workflow requires inputs, you can pass them in two ways.\n\nAs query parameters (simplest, with a GET request):\n\n$ curl \\\n  --request GET \"run.bump.sh/$ORGANIZATION_SLUG/$MCP_SERVER_SLUG/debug/$TOOL_NAME?city=paris&amp;format=celsius\" \\\n  --header \"Authorization: Token $DEBUG_TOKEN\"\n\n\nAs a JSON body (with a POST request):\n\n$ curl \\\n  --request POST \"run.bump.sh/$ORGANIZATION_SLUG/$MCP_SERVER_SLUG/debug/$TOOL_NAME\" \\\n  --header \"Authorization: Token $DEBUG_TOKEN\" \\\n  --header \"Content-Type: application/json\" \\\n  --data '{\"city\": \"paris\", \"format\": \"celsius\"}'\n\n\n\n\n\n  Debug sessions run on the production environment. Responses may include sensitive data. Handle them with care.\n\n\nRead the response\n\nThe debug endpoint returns a JSON object with two keys:\n\n\n  outputs: the final values returned to the AI agent after running the workflow.\n  trace: the full execution trace, showing every step the data plane ran.\n\n\nQuick check\n\nStart by looking at outputs. If the values are wrong or missing, dive into the trace to find where things went wrong.\n\nUnderstand the trace\n\nThe trace object contains:\n\n\n  Flow-level inputs and outputs: what the workflow received and what it returned.\n  Steps (step.run): each HTTP request the data plane executed, with the full request (method, URL, query, body) and the API response (status, body).\n  Actions (action.run): flow control decisions between steps. If your workflow uses conditions (retry, goto, end), this is where they are evaluated.\n\n\nExample\n\nHere is a shortened trace from our Weather MCP server. The workflow resolves a city name to coordinates, then fetches the current weather.\n\n{\n    \"outputs\": {\n        \"temperature\": \"18.2 °C\",\n        \"rain\": \"0.0\",\n        \"snowfall\": \"0.0\"\n    },\n    \"trace\": {\n        \"name\": \"flow.run\",\n        \"duration_ms\": 181.44,\n        \"flow_id\": \"weather_current\",\n        \"inputs\": {\n            \"city\": \"paris\"\n        },\n        \"outputs\": {\n            \"temperature\": \"18.2 °C\",\n            \"rain\": \"0.0\",\n            \"snowfall\": \"0.0\"\n        },\n        \"children\": [\n            {\n                \"name\": \"step.run\",\n                \"duration_ms\": 61.2,\n                \"step_id\": \"get_lat_lon\",\n                \"request\": {\n                    \"method\": \"get\",\n                    \"url\": \"https://nominatim.openstreetmap.org/search?q=paris&amp;format=jsonv2\",\n                    \"query\": { \"q\": \"paris\", \"format\": \"jsonv2\" }\n                },\n                \"response\": {\n                    \"status\": 200,\n                    \"body\": [\n                        { \"lat\": \"48.8534951\", \"lon\": \"2.3483915\", \"name\": \"Paris\" }\n                    ]\n                }\n            },\n            {\n                \"name\": \"action.run\",\n                \"duration_ms\": 0.13,\n                \"action\": { \"type\": \"next\" }\n            },\n            {\n                \"name\": \"step.run\",\n                \"duration_ms\": 118.45,\n                \"step_id\": \"get_weather_current\",\n                \"request\": {\n                    \"method\": \"get\",\n                    \"url\": \"https://api.open-meteo.com/v1/forecast?latitude=48.8534951&amp;longitude=2.3483915&amp;current=temperature_2m,rain,snowfall\",\n                    \"query\": {\n                        \"latitude\": \"48.8534951\",\n                        \"longitude\": \"2.3483915\",\n                        \"current\": \"temperature_2m,rain,snowfall\"\n                    }\n                },\n                \"response\": {\n                    \"status\": 200,\n                    \"body\": {\n                        \"current\": {\n                            \"temperature_2m\": 18.2,\n                            \"rain\": 0.0,\n                            \"snowfall\": 0.0\n                        }\n                    }\n                }\n            },\n            {\n                \"name\": \"action.run\",\n                \"duration_ms\": 0.12,\n                \"action\": { \"type\": \"next\" }\n            }\n        ]\n    }\n}\n\n\nSecurity and access\n\nTrace data is not persisted. Nothing is stored between requests.\n\nMaintainers, admins, and owners of your organization can create debug sessions. Session activity is visible on the history page.\n\n\n\n\n  Admins and owners can stop other members’ sessions."
        },
        {
          "id": "help-mcp-servers-deploy-workflows",
          "title": "Deploy workflows",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/mcp-servers/deploy-workflows/",
          "content": "What are deployments\n  Deploy from the dashboard\n  Deploy from the CLI\n  Deploy using the GitHub Action\n  Deployment states\n\n\nWhat are deployments\n\nSimilar to API deployments, a deployment is the processing by Bump.sh servers of a workflow document containing Arazzo or Flower workflows. After processing, workflows contained by this document are made available by your MCP server.\n\n\n\n\n  MCP servers always run workflows of the last deployment.\n\n\nDeploy from the dashboard\n\nYou can deploy workflow documents directly from your MCP server settings. To do so, click on the “Deploy new workflow document” button.\n\n\n\nDeploy from the CLI\n\nYou can deploy a workflow document using our CLI using the TBD command. The complete process is available on the dedicated CLI page.\n\nDeploy using the GitHub Action\n\nOur GitHub Action allows you to easily integrate Bump.sh into your projects.\n\nDeployment states\nYou can check the state of a deployment in the deployments page and verify which one is served by the MCP server.\n\n\n  \n    \n      State\n      Description\n    \n    \n      Active\n      Exposed by the MCP server.\n    \n    \n      Deployed\n      Previous deploy, no longer exposed by the MCP server.\n    \n    \n      Deploying\n      Being processed by Bump.sh.\n    \n    \n      Errored\n      Encountered an error during deployment."
        },
        {
          "id": "help-mcp-servers",
          "title": "MCP servers",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/mcp-servers/",
          "content": "What is MCP\n  What Bump.sh offers    \n      The data plane\n      Workflow specifications\n    \n  \n  Get started\n  Go further\n\n\nWhat is MCP\n\nThe Model Context Protocol (MCP) is an open standard that lets AI agents discover and call external tools. An MCP server exposes a set of tools that agents (ChatGPT, Claude, Cursor, etc.) can invoke to interact with APIs, databases, or any external service.\n\nWhat Bump.sh offers\n\nBump.sh lets you create MCP servers by declaring workflows in a simple document. You describe the API calls and their sequencing, and Bump.sh generates a fully hosted MCP server that executes them.\n\nThis approach is declarative: you define what the server should do, not how to run it. There is no code to write, no server to deploy, no infrastructure to manage. Because every execution follows your workflow definition exactly, the behavior is deterministic and predictable, which is critical when AI agents interact with real APIs.\n\nThe data plane\n\nAll API calls are executed by an isolated component called the data plane. The AI agent never calls your APIs directly. Instead, it invokes a tool on your MCP server, and the data plane runs the corresponding workflow: resolving secrets, executing HTTP requests, and returning only the declared outputs to the agent.\n\nThis architecture keeps credentials and sensitive API responses away from the LLM. See Security for details.\n\nWorkflow specifications\n\nWorkflows can be written in two formats:\n\n\n  Flower: a lightweight specification designed by Bump.sh. Ideal for small projects, quick prototyping, or workflows calling APIs for which you don’t have an OpenAPI document.\n  Arazzo: the workflow specification from the OpenAPI Initiative. Better suited for complex projects that need to reference existing OpenAPI documents.\n\n\nBoth formats support multi-step sequences, conditional logic (retry, goto, end), runtime expressions, and secrets.\n\nGet started\n\n\n  Create an MCP server from your dashboard.\n  Deploy a workflow document that describes the API calls your server can perform.\n  Share the server URL so that end-users can add it to their AI tool.\n\n\nGo further\n\n\n  Secrets: store API keys and tokens so they are never exposed in your workflow documents.\n  Access management: control who can use your MCP server.\n  Debug sessions: inspect execution traces to troubleshoot workflows step by step.\n  Runtime expressions: reference dynamic data between steps.\n  Security: understand the architecture and data handling guarantees.\n\n\n\n  MCP servers are in closed beta. Contact us to be among our first users."
        },
        {
          "id": "help-mcp-servers-runtime-expressions",
          "title": "Runtime expressions",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/mcp-servers/runtime-expressions/",
          "content": "Expression variables    \n      Flow inputs ($inputs)\n      Current step response ($response)\n      Current step request ($request)\n      Current step outputs ($outputs)\n      Previous step results ($steps)\n      Server secrets ($secrets)\n      Current user token ($current_user.token)\n    \n  \n  Accessing data    \n      Dot notation\n      JMESPath        \n          Projections ([*])\n          Indexing ([n])\n          Slicing ([start:end])\n          Filtering ([?condition])\n          Functions\n        \n      \n    \n  \n  String interpolation\n  Conditions\n  Complete example\n\n\nRuntime expressions let you dynamically reference values during workflow execution: inputs from the caller, responses from previous steps, secrets, and more. They are used in both Flower and Arazzo workflow definitions.\n\nExpressions start with a $ prefix and are resolved at runtime.\n\nExpression variables\n\nFlow inputs ($inputs)\n\nAccess values passed by the caller when invoking a flow.\n\nquery:\n  q: $inputs.city\n\n\nNested access and array indexing are supported (see Accessing data for all syntaxes):\n\n\n  \n    \n      Expression\n      Description\n    \n  \n  \n    \n      $inputs.city\n      Top-level input\n    \n    \n      $inputs.address.zipcode\n      Nested object property\n    \n    \n      $inputs.items.0.name\n      Array element (dot notation)\n    \n    \n      $inputs.items[0].name\n      Array element (JMESPath)\n    \n  \n\n\nCurrent step response ($response)\n\nReference the HTTP response of the current step.\n\noutputs:\n  lat: $response.body.0.lat\n  lon: $response.body.0.lon\n  status: $response.status\n\n\n\n  \n    \n      Path\n      Description\n    \n  \n  \n    \n      $response.body\n      Parsed response body (JSON)\n    \n    \n      $response.body.&lt;path&gt;\n      Deep access into the body\n    \n    \n      $response.status\n      HTTP status code\n    \n    \n      $response.headers\n      Response headers\n    \n    \n      $statusCode\n      Shortcut for $response.status\n    \n    \n      $status\n      Shortcut for $response.status\n    \n  \n\n\nCurrent step request ($request)\n\nReference the request sent for the current step.\n\n\n  \n    \n      Path\n      Description\n    \n  \n  \n    \n      $request.method\n      HTTP method (GET, POST, etc.)\n    \n    \n      $request.url\n      Full URL including query string\n    \n    \n      $request.query\n      Query parameters\n    \n    \n      $request.headers\n      Request headers\n    \n    \n      $request.body\n      Request body\n    \n    \n      $method\n      Shortcut for $request.method\n    \n    \n      $url\n      Shortcut for $request.url\n    \n  \n\n\nCurrent step outputs ($outputs)\n\nReference resolved outputs of the current step. Useful when an output depends on another output’s value.\n\noutputs:\n  lat: $response.body.0.lat\n  coordinates: \"$outputs.lat, $response.body.0.lon\"\n\n\nPrevious step results ($steps)\n\nAccess the request, response, or outputs of a previously executed step.\n\nsteps:\n  - id: get_lat_lon\n    request:\n      method: GET\n      url: https://nominatim.openstreetmap.org/search\n      query:\n        q: $inputs.city\n        format: jsonv2\n    outputs:\n      lat: $response.body.0.lat\n\n  - id: get_weather\n    request:\n      method: GET\n      url: https://api.open-meteo.com/v1/forecast\n      query:\n        latitude: $steps.get_lat_lon.outputs.lat\n\n\nThe pattern is $steps.&lt;step_id&gt;.&lt;property&gt;.&lt;path&gt; where &lt;property&gt; is one of:\n\n\n  \n    \n      Pattern\n      Description\n    \n  \n  \n    \n      $steps.&lt;id&gt;.outputs.&lt;key&gt;\n      A resolved output value\n    \n    \n      $steps.&lt;id&gt;.response.body.&lt;path&gt;\n      Response body access\n    \n    \n      $steps.&lt;id&gt;.response.status\n      Response status code\n    \n    \n      $steps.&lt;id&gt;.request.url\n      Request URL\n    \n  \n\n\nServer secrets ($secrets)\n\nReference secrets stored on the server. Secret values are never included in workflow outputs.\n\nheaders:\n  Authorization: \"Bearer $secrets.API_KEY\"\n\n\nSecret names must start with a letter, followed by letters, digits, or underscores (e.g. API_KEY, myToken2).\n\n\n  See Secrets for how to configure secrets on your MCP server.\n\n\nCurrent user token ($current_user.token)\n\nReturns the OAuth token of the currently authenticated user. Only available on private MCP servers. Useful for forwarding the user’s identity to external APIs.\n\nheaders:\n  Authorization: \"Bearer $current_user.token\"\n\n\nAccessing data\n\nTwo syntaxes are available:\n\n\n  Dot notation covers property access and array indexing. Sufficient for most use cases.\n  JMESPath adds projections, filtering, slicing, and functions for advanced queries.\n\n\nBoth work with any expression variable ($response, $inputs, $steps, etc.).\n\nDot notation\n\n$response.body.current.temperature_2m     # object property\n$response.body.0.lat                      # array element (0-based)\n$response.body.users.0.address.city       # combined\n\n\nJMESPath\n\n\n  JMESPath support is a Bump.sh extension. It is not part of the Arazzo specification.\n\n\nWhen dot notation is not enough, Bump.sh supports JMESPath, a query language for JSON. An expression is automatically treated as JMESPath when it contains bracket syntax ([*], [0], [?...], [0:3]) or a function call (avg(...), length(...), etc.).\n\nProjections ([*])\n\nExtract a property from every element of an array:\n\n# Given response body: {\"users\": [{\"name\": \"Alice\"}, {\"name\": \"Bob\"}]}\noutputs:\n  names: $response.body.users[*].name\n  # -&gt; [\"Alice\", \"Bob\"]\n\n\nIndexing ([n])\n\noutputs:\n  first_user: $response.body.users[0].name\n  # -&gt; \"Alice\"\n\n\nSlicing ([start:end])\n\noutputs:\n  first_three: $response.body.items[0:3]\n\n\nFiltering ([?condition])\n\nSelect array elements matching a condition:\n\n# Given: [{\"name\": \"Alice\", \"age\": 25}, {\"name\": \"Bob\", \"age\": 35}]\noutputs:\n  seniors: $response.body.users[?age &gt; `30`].name\n  # -&gt; [\"Bob\"]\n\n\n\n  Literal numbers in filter conditions must be wrapped in backticks (`30`), as per JMESPath syntax.\n\n\nFunctions\n\noutputs:\n  avg_temp: avg($response.body.hourly.temperature_2m)\n  count: length($response.body.items)\n\n\nNumeric\n\n\n  \n    \n      Function\n      Description\n      Example\n    \n  \n  \n    \n      abs(val)\n      Absolute value\n      abs($inputs.delta)\n    \n    \n      avg(arr)\n      Average of a numeric array\n      avg($response.body.temps[*])\n    \n    \n      ceil(num)\n      Round up\n      ceil($response.body.score)\n    \n    \n      floor(num)\n      Round down\n      floor($response.body.score)\n    \n    \n      sum(arr)\n      Sum of a numeric array\n      sum($response.body.prices[*])\n    \n  \n\n\nArray and object\n\n\n  \n    \n      Function\n      Description\n      Example\n    \n  \n  \n    \n      length(val)\n      Length of array, object, or string\n      length($response.body.items)\n    \n    \n      keys(obj)\n      Keys of an object\n      keys($response.body.config)\n    \n    \n      values(obj)\n      Values of an object\n      values($response.body.config)\n    \n    \n      sort(arr)\n      Sort an array\n      sort($response.body.scores[*])\n    \n    \n      sort_by(arr, &amp;expr)\n      Sort by expression\n      sort_by($response.body.users[*], &amp;age)\n    \n    \n      max(arr)\n      Maximum value\n      max($response.body.scores[*])\n    \n    \n      max_by(arr, &amp;expr)\n      Max by expression\n      max_by($response.body.users[*], &amp;age)\n    \n    \n      min(arr)\n      Minimum value\n      min($response.body.scores[*])\n    \n    \n      min_by(arr, &amp;expr)\n      Min by expression\n      min_by($response.body.users[*], &amp;age)\n    \n    \n      reverse(arr)\n      Reverse array or string\n      reverse($response.body.items)\n    \n    \n      flatten(arr)\n      Flatten nested arrays (1 level)\n      flatten($response.body.nested)\n    \n    \n      to_array(val)\n      Wrap value in an array\n      to_array($inputs.value)\n    \n  \n\n\nString\n\n\n  \n    \n      Function\n      Description\n      Example\n    \n  \n  \n    \n      to_string(val)\n      Convert to string\n      to_string($response.body.id)\n    \n    \n      to_number(val)\n      Convert to number\n      to_number($response.body.count)\n    \n  \n\n\nType\n\n\n  \n    \n      Function\n      Description\n      Example\n    \n  \n  \n    \n      type(val)\n      Returns the JSON type of a value\n      type($response.body.result)\n    \n  \n\n\n\n  Nested function calls (like length(sort($ref[*]))) are not supported.\n\n\nString interpolation\n\nMultiple expressions can coexist in a single string:\n\noutputs:\n  summary: \"$steps.get_weather.outputs.temperature in $inputs.city\"\n  # -&gt; \"22.5 in Marseille\"\n\n\n\n  String interpolation only works with dot notation expressions. JMESPath expressions (containing [*], [0], functions, etc.) are resolved as a whole and cannot be combined with other expressions in the same string.\n\n\nConditions\n\nExpressions inside when clauses are resolved, then evaluated with standard operators.\n\nactions:\n  - when: \"$statusCode == 200\"\n    do: next\n  - when: \"$statusCode == 202\"\n    do: retry\n    wait: 2\n  - when: \"$response.body.status == 'pending'\"\n    do: retry\n\n\nSupported operators: ==, !=, &gt;, &lt;, &gt;=, &lt;=, AND, OR, !.\n\nComplete example\n\nflower: \"0.1\"\nid: weather\ntitle: Get the weather for a specific city\n\nflows:\n  - id: weather_forecast\n    description: Get the weather forecast for a specific city\n    inputs:\n      properties:\n        city:\n          type: string\n      required:\n        - city\n\n    steps:\n      - id: get_lat_lon\n        request:\n          method: GET\n          url: https://nominatim.openstreetmap.org/search\n          query:\n            q: $inputs.city\n            format: jsonv2\n        outputs:\n          lat: $response.body.0.lat\n          lon: $response.body.0.lon\n\n      - id: get_weather_forecast\n        request:\n          method: GET\n          url: https://api.open-meteo.com/v1/forecast\n          query:\n            latitude: $steps.get_lat_lon.outputs.lat\n            longitude: $steps.get_lat_lon.outputs.lon\n            daily: temperature_2m_min,temperature_2m_max\n            hourly: temperature_2m\n        outputs:\n          temperature_avg: avg($response.body.hourly.temperature_2m)\n          temperature_min: $response.body.daily.temperature_2m_min\n          temperature_max: $response.body.daily.temperature_2m_max\n        actions:\n          - when: \"$statusCode == 200\"\n            do: next\n          - when: \"$statusCode == 429\"\n            do: retry\n            wait: 10\n\n    outputs:\n      temperature_avg: $steps.get_weather_forecast.outputs.temperature_avg\n      temperature_min: $steps.get_weather_forecast.outputs.temperature_min\n      temperature_max: $steps.get_weather_forecast.outputs.temperature_max"
        },
        {
          "id": "help-mcp-servers-secrets",
          "title": "Secrets",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/mcp-servers/secrets/",
          "content": "Creating a secret\n  Using a secret in a workflow\n  Storage and security\n\n\nSecrets let you store sensitive values, such as API keys or tokens, separately from your workflow documents. They are injected at runtime so that your MCP server can authenticate with external APIs without exposing credentials in plain text.\n\nCreating a secret\n\nGo to the Secrets section of your MCP server settings, then enter a name and a value.\n\n\n\n\n  Secrets are scoped to a single MCP server. They can only be accessed by that server’s workflows.\n\n\nUsing a secret in a workflow\n\nReference a secret with the $secrets.{name} expression wherever you would otherwise hard-code a sensitive value:\n\nflows:\n  - id: book-train-trip\n    description: ...\n    inputs: {}\n    steps: []\n    security: $secrets.booking-api-key\n\n\nStorage and security\n\nSecret values are encrypted at rest (AES-256-GCM) and stored on an isolated infrastructure, separate from the main Bump.sh application. The Bump.sh application itself never has access to decrypted secret values.\n\nAt runtime, secrets are decrypted in memory only for the duration of a workflow execution. They are never written to logs, included in workflow outputs, or returned in API responses."
        },
        {
          "id": "help-mcp-servers-security",
          "title": "Security",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/mcp-servers/security/",
          "content": "Architecture\n  What each component can access\n  Data handling\n\n\nBump.sh MCP servers are designed so that sensitive data never leaves the execution environment and is never exposed to the LLM. This page explains the architecture and the security measures in place.\n\nArchitecture\n\nBump.sh MCP servers rely on two separate components:\n\n\n  The Bump.sh application handles server configuration, workflow deployments, and access management. It never processes API calls or accesses secret values.\n  The data plane is an isolated infrastructure that executes your workflows at runtime. It is the only component that resolves secrets, performs HTTP requests to external APIs, and handles responses.\n\n\nBecause the data plane executes API requests on behalf of the LLM, the LLM itself never directly calls APIs. This means credentials, tokens, and sensitive response data are never exposed to the model.\n\nThese two components communicate over encrypted channels (TLS) and are deployed independently.\n\n\n  The default data plane is hosted on Bump.sh infrastructure. On Custom plans, it can be installed on-premise in your own infrastructure.\n\n\nWhat each component can access\n\n\n  \n    \n       \n      Bump.sh application\n      Data plane\n    \n  \n  \n    \n      Server configuration\n      Yes\n      Yes\n    \n    \n      Workflow documents\n      Yes\n      Yes\n    \n    \n      Secret values (decrypted)\n      No\n      Yes, in memory at runtime only\n    \n    \n      API requests and responses\n      No\n      Yes, during execution only\n    \n    \n      Execution logs with sensitive data\n      No\n      No\n    \n  \n\n\nData handling\n\nThe data plane does not persist sensitive data. Specifically:\n\n\n  Secret values are encrypted at rest (AES-256-GCM) and only decrypted in memory for the duration of a workflow execution. See Secrets for details.\n  API responses are processed in memory and discarded after each execution. They are not stored or forwarded to the Bump.sh application.\n  Logs never contain secret values, request bodies, or response payloads."
        },
        {
          "id": "help-mcp-servers-specification-support-arazzo-support",
          "title": "Arazzo support",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/mcp-servers/specification-support/arazzo-support/",
          "content": "When to use Arazzo\n\n\nArazzo is the workflow specification from the OpenAPI Initiative. It provides a standardized, machine-readable format for describing sequences of API calls, with support for passing data between steps, conditional branching, success criteria, and failure handling.\n\nIf you need help writing efficient Arazzo workflows, discover our Arazzo complete guide.\n\nWhen to use Arazzo\n\nWe recommend using it when you already have OpenAPI documents describing your APIs, and if you want a vendor-neutral, standardized format usable by other tools. For simpler projects or APIs without OpenAPI documents, consider Flower, our lightweight alternative."
        },
        {
          "id": "help-mcp-servers-specification-support-flower-support",
          "title": "Flower support",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/mcp-servers/specification-support/flower-support/",
          "content": "Quick example\n  Root properties\n  Flows    \n      Inputs\n      Outputs\n    \n  \n  Steps    \n      Request\n      Step outputs\n    \n  \n  Actions\n  Runtime expressions\n  Full example: API diff with retry\n\n\nFlower is the internal specification powering the Bump.sh data plane. It was designed to keep workflow files as simple as possible, with no external dependencies. For a standardized approach to defining API workflows, we recommend using Arazzo, the workflow specification from the OpenAPI Initiative.\n\nThat said, thanks to its simplicity, Flower can replace Arazzo for small projects that don’t require linking to OpenAPI files, or to describe workflows calling external APIs for which you don’t have an OpenAPI document.\n\nThe specification, along with a JSON Schema for validation and a set of utilities, is available on GitHub.\n\n\n  New to Flower? Follow the step-by-step getting started guide to write your first workflow file.\n\n\nQuick example\n\nThis workflow looks up the current weather for a city by chaining two API calls:\n\nflower: \"0.1\"\nid: weather\ntitle: Get the weather for a specific city\n\nflows:\n  - id: weather_current\n    description: Get the current weather for a specific city\n    inputs:\n      properties:\n        city:\n          type: string\n      required:\n        - city\n\n    steps:\n      - id: get_lat_lon\n        request:\n          method: GET\n          url: https://nominatim.openstreetmap.org/search\n          query:\n            q: $inputs.city\n            format: jsonv2\n        outputs:\n          lat: $response.body.0.lat\n          lon: $response.body.0.lon\n\n      - id: get_weather_current\n        request:\n          method: GET\n          url: https://api.open-meteo.com/v1/forecast\n          query:\n            latitude: $steps.get_lat_lon.outputs.lat\n            longitude: $steps.get_lat_lon.outputs.lon\n            current: temperature_2m,is_day,wind_speed_10m\n        outputs:\n          temperature: $response.body.current.temperature_2m\n\n    outputs:\n      temperature: $steps.get_weather_current.outputs.temperature\n\n\nA Flower document defines a set of flows, each composed of sequential steps. Each step makes an HTTP request and extracts outputs that subsequent steps can reference. Flower documents can be written in YAML or JSON. YAML is recommended for readability.\n\nRoot properties\n\n\n  \n    \n      Property\n      Required\n      Description\n    \n  \n  \n    \n      flower\n      Yes\n      Specification version. Currently \"0.1\".\n    \n    \n      id\n      Yes\n      Unique identifier for this workflow set. Use lowercase letters, numbers, and hyphens.\n    \n    \n      title\n      No\n      Human-readable title.\n    \n    \n      description\n      No\n      What this workflow set does.\n    \n    \n      flows\n      Yes\n      Array of flow definitions.\n    \n  \n\n\nflower: \"0.1\"\nid: weather-service\ntitle: Weather Information Service\ndescription: Provides current weather and forecast information\nflows:\n  - ...\n\n\nFlows\n\nA flow accepts inputs, executes a series of steps, and produces outputs.\n\n\n  \n    \n      Property\n      Required\n      Description\n    \n  \n  \n    \n      id\n      Yes\n      Unique identifier within the workflow set.\n    \n    \n      description\n      No\n      What this flow does.\n    \n    \n      inputs\n      No\n      Input parameters, defined using JSON Schema format.\n    \n    \n      steps\n      Yes\n      Array of step definitions, executed sequentially.\n    \n    \n      outputs\n      No\n      Values extracted from step results using runtime expressions.\n    \n  \n\n\nInputs\n\nInputs use JSON Schema to validate the parameters a flow accepts.\n\ninputs:\n  properties:\n    city:\n      type: string\n      description: The name of the city\n    format:\n      type: string\n      enum: [celsius, fahrenheit]\n      default: celsius\n  required:\n    - city\n\n\nSupported JSON Schema properties: type (string, number, boolean, array, object), description, enum, default, required.\n\nOutputs\n\nFlow outputs extract data from step results. Each key maps to a runtime expression.\n\noutputs:\n  temperature: $steps.get-weather.outputs.temperature\n  city_name: $steps.get-coords.outputs.city\n\n\nSteps\n\nA step represents a single HTTP request. Steps run sequentially within a flow.\n\n\n  \n    \n      Property\n      Required\n      Description\n    \n  \n  \n    \n      id\n      Yes\n      Unique identifier within the flow. Referenced by subsequent steps.\n    \n    \n      request\n      Yes\n      The HTTP request to execute.\n    \n    \n      outputs\n      No\n      Values extracted from the response.\n    \n    \n      actions\n      No\n      Conditional logic for flow control after this step.\n    \n  \n\n\nRequest\n\n\n  \n    \n      Property\n      Required\n      Description\n    \n  \n  \n    \n      method\n      Yes\n      HTTP method: GET, POST, PUT, PATCH, DELETE, HEAD.\n    \n    \n      url\n      Yes\n      Endpoint URL. Supports runtime expressions.\n    \n    \n      headers\n      No\n      HTTP headers as key-value pairs. Values can use runtime expressions.\n    \n    \n      query\n      No\n      Query parameters as key-value pairs.\n    \n    \n      body\n      No\n      Request body (object or string). Typically used with POST, PUT, PATCH.\n    \n  \n\n\nsteps:\n  - id: create_diff\n    request:\n      method: POST\n      url: https://bump.sh/api/v1/diffs\n      body:\n        previous_url: \"$inputs.left_url\"\n        url: \"$inputs.right_url\"\n    outputs:\n      id: $response.body.id\n\n\nStep outputs\n\nOutputs extract values from the HTTP response using runtime expressions. They become available to subsequent steps via $steps.&lt;step_id&gt;.outputs.&lt;key&gt;.\n\noutputs:\n  lat: $response.body.0.lat\n  lon: $response.body.0.lon\n\n\nActions\n\nActions control flow execution after a step completes. They are evaluated in order: the first matching action is executed.\n\nEach action has two properties:\n\n  when (optional): A condition expression. If omitted, the action always matches.\n  do (required): The action type.\n\n\n\n  \n    \n      Action type\n      Description\n      Extra properties\n    \n  \n  \n    \n      next\n      Continue to the next step.\n       \n    \n    \n      retry\n      Retry the current step.\n      wait (seconds, default: 5), max_retry (default: 5)\n    \n    \n      goto\n      Jump to a different step.\n      step (target step ID)\n    \n    \n      end\n      Terminate the flow and return outputs.\n       \n    \n  \n\n\nactions:\n  - when: \"$statusCode == 202\"\n    do: retry\n    wait: 2\n    max_retry: 5\n  - when: \"$response.body.error\"\n    do: end\n  - do: next\n\n\nRuntime expressions\n\nRuntime expressions reference dynamic data during execution. They use a $ prefix with dot notation.\n\n\n  \n    \n      Expression\n      Description\n    \n  \n  \n    \n      $inputs.city\n      Flow input parameter.\n    \n    \n      $response.body.field\n      Current step’s response body.\n    \n    \n      $response.body.0.lat\n      Array index access.\n    \n    \n      $steps.step_id.outputs.key\n      Output from a previous step.\n    \n    \n      $statusCode\n      HTTP status code of the current response.\n    \n    \n      $secrets.name\n      Secret value (never exposed in outputs).\n    \n  \n\n\nFor the complete reference (JMESPath queries, conditions, resolution order), see Runtime expressions.\n\nFull example: API diff with retry\n\nThis workflow creates an API diff and polls until the result is ready:\n\nflower: \"0.1\"\nid: bump\ntitle: Bump.sh API diff\n\nflows:\n  - id: diff\n    description: Get a diff between 2 API documents\n    inputs:\n      properties:\n        left_url:\n          type: string\n          description: First API contract URL\n        right_url:\n          type: string\n          description: Second API contract URL\n      required:\n        - left_url\n        - right_url\n\n    steps:\n      - id: create_diff\n        request:\n          method: POST\n          url: https://bump.sh/api/v1/diffs\n          body:\n            previous_url: \"$inputs.left_url\"\n            url: \"$inputs.right_url\"\n        outputs:\n          id: $response.body.id\n\n      - id: get_diff\n        request:\n          method: GET\n          url: https://bump.sh/api/v1/diffs/$steps.create_diff.outputs.id\n        outputs:\n          result: $response.body.markdown\n          breaking: $response.body.breaking\n        actions:\n          - when: \"$statusCode == 202\"\n            do: retry\n            wait: 2\n          - do: next\n\n    outputs:\n      breaking: $steps.get_diff.outputs.breaking\n      result: $steps.get_diff.outputs.result"
        },
        {
          "id": "help-mcp-servers-use-mcp-server",
          "title": "Use an MCP server",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/mcp-servers/use-mcp-server/",
          "content": "ChatGPT\n  Claude\n  Cursor\n  MCPJam (debug tool)\n\n\nTo use an MCP server, the most common way is to add it to your IDE or AI tool. Some offer “one-click” addition, while others require a bit of configuration. If your tool is not listed, have a look at their documentation: MCP server integration might be supported.\n\n\n  If your Bump.sh MCP server is private, don’t forget to select OAuth as the authentication type while adding the server to your tool.\n\n\nChatGPT\nTo add an MCP server to ChatGPT, go to Settings -&gt; Apps -&gt; Advanced settings -&gt; Create app, and fill the MCP server URL field.\n\n\n\n\n  For now, adding custom Apps is only available in ChatGPT paid plans.\n\n\nClaude\nTo add an MCP server to Claude (Desktop or web), go to Settings -&gt; Connectors -&gt; Add custom connector, and fill the URL field.\n\n\n\n\n  For now, adding custom connectors is only available in Claude paid plans.\n\n\nCursor\nTo add an MCP server to Cursor, go to Cursor Settings -&gt; Tools &amp; MCP -&gt; New MCP server. It will open the mcp.json file, that you need to fill as below:\n{\n  \"mcpServers\": {\n    \"My Bump.sh MCP server\": {\n      \"url\": \"https://run.bump.sh/my-org/my-mcp-server/mcp\"\n    }\n  }\n}\n\n\nMCPJam (debug tool)\nMCPJam is a great tool to debug your workflows. It allows you to interact with the MCP server and analyze the requests sent by the LLM to the MCP server. To add an MCP server to MCPJam, click on “Add Server”, and fill the “Connection Type” URL field."
        },
        {
          "id": "help-mcp-servers-write-your-first-flower-workflow",
          "title": "Write your first Flower workflow",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/mcp-servers/write-your-first-flower-workflow/",
          "content": "How to design your workflows\n  Generate a Flower document using AI\n  Learn the basics    \n      Set the global structure\n      Define your first flow\n      Add a step\n      Define global outputs\n    \n  \n  Define a reliable, multi-step workflow    \n      Chain steps together\n      Add conditions and handle errors\n      Authenticate with protected APIs\n    \n  \n  Complete example\n  Next steps\n\n\nThis guide provides a step-by-step explanation of how to define an API workflow to achieve real-world goals using the Flower specification. For a complete reference of all properties, see Flower support.\n\nHow to design your workflows\n\nBefore writing a single line of YAML, take the time to define your use cases. The first question you should ask yourself is: what tasks most of my users (either humans or agents) want to achieve using my APIs, or my UI? It could be looking at analytics, checking the gross revenue for the last 30 days, finding a product in a catalog, …\n\nOnce that question is behind you, think about:\n\n  The API call chain needed to achieve the workflow. Does your API currently have the capabilities to execute that workflow?\n  The inputs the workflow will need.\n  What are the expected outputs from the user/AI?\n\n\nIf that’s the first time you design workflows for an MCP server, start simple: focus on the two or three workflows that will be the most useful to your users. Ideally, these first workflows are read-only.\n\n\n  The closer your workflows stay to real user needs, the more effectively AI tools will use them, and the less guesswork they’ll need.\n\n\nGenerate a Flower document using AI\n\nWe recommend following this getting started to get a hand on the specification, but you can also generate a Flower document using an LLM. To do so:\n\n  Describe what you want to achieve with these workflows (book a train ticket, retrieve analytics from different sources, …).\n  Provide a link to your API documentation or your API document.\n  Give the LLM context about the Flower specification:\n    \n      Flower support reference\n      Flower schema\n      Flower document examples\n    \n  \n\n\nLearn the basics\n\nSet the global structure\n\nCreate a new YAML file and add the following root properties:\n\nflower: \"0.1\"\nid: weather \ntitle: Get the weather for a specific city \ndescription: &gt;\n  Fetches the current weather for any city in the world.\nflows:\n  - ...\n\n\n\n  flower declares the specification version. Last version is \"0.1\".\n  id is a unique identifier for this workflow set. Use lowercase letters, numbers, and hyphens.\n  title and description help AI agents and LLMs understand what can be achieved with your MCP server.\n  flows holds the list of flows. You will define them in the next steps.\n\n\nDefine your first flow\n\nA flow is a sequence of API calls (or steps in Flower). Replace the placeholder - ... from above with your first flow. Try to keep it simple for your first document, so you easily get the full picture.\n\nflows:\n  - id: weather_current\n    description: &gt;\n      Get the current temperature and rain for a specific city.\n      Returns temperature in Celsius and current rain in mm.\n    inputs:\n      properties:\n        city:\n          type: string\n          description: The name of the city (e.g. Paris, Tokyo)\n      required:\n        - city\n    steps:\n      - ...\n\n\n\n  id identifies the flow. It’s also the tool name exposed by your MCP server.\n  description is read by AI agents and LLMs to decide when and how to invoke the flow. It’s dedicated to non-human readers: describe what it returns, not just what it does.\n  inputs uses JSON Schema to declare what parameters the flow accepts. List mandatory ones under required.\n\n\n\n  A precise description makes a big difference. Vague descriptions lead AI tools to call the wrong flow or pass incorrect inputs.\n\n\nAdd a step\n\nSteps are where the HTTP calls happen. Each step makes one request and optionally extracts values from the response. A flow is usually made of multiple steps, but we’ll only add one for now to focus on its content. Replace the placeholder - ... from above with your first step:\n\nflows:\n  - id: weather_current\n    ...\n    steps:\n      - id: get_lat_lon\n        request:\n          method: GET\n          url: https://nominatim.openstreetmap.org/search\n          headers:\n            Authorization: \"Bearer $secrets.my-api-key\"\n          query:\n            q: $inputs.city\n            format: jsonv2\n        outputs:\n          lat: $response.body.0.lat\n          lon: $response.body.0.lon\n\n\nA few things to notice:\n\n  $secrets.my-api-key injects a secret stored on Bump.sh.\n  $inputs.city is a runtime expression that injects a value provided either by an AI agent or a human through its LLM. Expressions always start with $. See Runtime expressions for the full reference.\n  query maps to URL query parameters. The request above resolves to GET /search?q=Paris&amp;format=jsonv2 when city is Paris.\n  outputs extracts values from the response. $response.body.0.lat reads the lat property from the first element of the response array.\n\n\nDefine global outputs\n\nFlows return data to the API tool through a top-level outputs map. Add one at the end of your flow:\n\nflows:\n  - id: weather_current\n    steps:\n      - id: get_lat_lon\n        ...\n    outputs:\n      lat: $steps.get_lat_lon.outputs.lat\n      lon: $steps.get_lat_lon.outputs.lon\n\n\n$steps.get_lat_lon.outputs.lat follows the pattern $steps.&lt;step_id&gt;.outputs.&lt;key&gt;.\n\n\n  Even if the API returns more information, only values declared as outputs are returned to the AI tool.\n\n\nCongratulations! You have your first valid, deployable Flower document. It calls one API and returns two values. Let’s make it more useful and robust.\n\nDefine a reliable, multi-step workflow\n\nChain steps together\n\nWorkflows power comes from passing data between steps to achieve a real business goal. Reference a previous step’s outputs using $steps.&lt;step_id&gt;.outputs.&lt;key&gt; in the next step’s request.\n\nAdd a second step that takes the coordinates from get_lat_lon and calls the weather API:\n\n    steps:\n      - id: get_lat_lon\n        ...\n        outputs:\n          lat: $response.body.0.lat\n          lon: $response.body.0.lon\n      - id: get_weather_current\n        request:\n          method: GET\n          url: https://api.open-meteo.com/v1/forecast\n          query:\n            latitude: $steps.get_lat_lon.outputs.lat\n            longitude: $steps.get_lat_lon.outputs.lon\n            current: temperature_2m,wind_speed_10m,rain\n            timezone: Europe/Berlin\n        outputs:\n          temperature: $response.body.current.temperature_2m\n          rain: $response.body.current.rain\n    outputs:\n      temperature: $steps.get_weather_current.outputs.temperature\n      rain: $steps.get_weather_current.outputs.rain\n\n\nSteps run sequentially. Each step can reference the outputs of any step that ran before it.\n\nAdd conditions and handle errors\n\nBy default, a step completes and the flow moves on. actions let you change that behavior based on the response. They are evaluated in order: the first matching one is executed.\n\n    steps:\n      - id: get_lat_lon\n        ...\n        actions:\n          - when: \"$statusCode == 404\"\n            do: end\n          - when: \"$statusCode == 429\"\n            do: retry\n            wait: 5\n            max_retry: 3\n          - do: next\n\n\nLet’s break down the actions:\n\n  If the API returns a 404, the flow stops here.\n  If the API returns a 429, the step is retried after 5 seconds. 3 retries maximum.\n  Otherwise, the next step starts.\n\n\nYou can have conditions on response body values, not just status codes:\n\nactions:\n  - when: \"$response.body.error == true\"\n    do: end\n  - do: next\n\n\n\n  \n    \n      Action\n      What it does\n    \n  \n  \n    \n      end\n      Terminates the flow and returns collected outputs so far.\n    \n    \n      retry\n      Re-runs the same step. wait (seconds) and max_retry are optional.\n    \n    \n      next\n      Continues to the following step.\n    \n    \n      goto\n      Jumps to a specific step by its id.\n    \n  \n\n\n\n  The last action in the list is typically a catch-all with no when clause.\n\n\nAuthenticate with protected APIs\n\nIt’s not the case in our example, but if the API you’re calling requires authentication, create a secret on Bump.sh to store the key securely.\n\nReference it with the $secrets.{name} expression. You can also use $currentUser.token to directly retrieve the authenticated user’s token.\n\nFor example, if the API expects the key in a request header, add it in the step request.headers:\n\nflows:\n  - id: weather_current\n    steps:\n      - id: fetch_weather\n        request:\n          method: GET\n          url: https://api.example.com/weather\n          headers:\n            Authorization: \"Bearer $secrets.my-api-key\"\n\n\n\n  The secret is injected at runtime and never sent to the AI tool.\n\n\nComplete example\n\nHere is the full workflow built step by step in this guide:\n\nflower: \"0.1\"\nid: weather\ntitle: Get the weather for a specific city\ndescription: &gt;\n  Fetches the current weather for any city in the world.\n\nflows:\n  - id: weather_current\n    description: &gt;\n      Get the current temperature and rain for a specific city.\n      Returns temperature in Celsius and rain in mm.\n    inputs:\n      properties:\n        city:\n          type: string\n          description: The name of the city (e.g. Paris, Tokyo)\n      required:\n        - city\n\n    steps:\n      - id: get_lat_lon\n        request:\n          method: GET\n          url: https://nominatim.openstreetmap.org/search\n          query:\n            q: $inputs.city\n            format: jsonv2\n        outputs:\n          lat: $response.body.0.lat\n          lon: $response.body.0.lon\n        actions:\n          - when: \"$statusCode == 404\"\n            do: end\n          - do: next\n\n      - id: get_weather_current\n        request:\n          method: GET\n          url: https://api.open-meteo.com/v1/forecast\n          query:\n            latitude: $steps.get_lat_lon.outputs.lat\n            longitude: $steps.get_lat_lon.outputs.lon\n            current: temperature_2m,wind_speed_10m,rain\n            timezone: Europe/Berlin\n        outputs:\n          temperature: $response.body.current.temperature_2m\n          rain: $response.body.current.rain\n        actions:\n          - when: \"$statusCode == 429\"\n            do: retry\n            wait: 5\n            max_retry: 3\n          - do: next\n\n    outputs:\n      temperature: $steps.get_weather_current.outputs.temperature\n      rain: $steps.get_weather_current.outputs.rain\n\n\nNext steps\n\n\n  Deploy this file to your MCP server.\n  Store API keys safely using secrets.\n  Add more flows to the same file following the same pattern.\n  Learn how to do more complex data transformation using Runtime expressions.\n  Read the full Flower specification reference for all available properties and options."
        },
        {
          "id": "help-organizations-billing",
          "title": "Billing",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/organizations/billing/",
          "content": "Invoices    \n      Edit your billing information\n      Invoices\n      Taxes\n    \n  \n  Payment issues\n\n\nThe Billing section of your account provides access to the following items:\n\n\n  information about your current plan\n  your invoice history\n  your billing details\n\n\nBump.sh offers monthly or annual billing. We use Stripe to handle all payments, so we never store your bank details.\n\n\n  Payment is made by credit card only. We are considering offering alternative payment methods in the future.\n\n\nInvoices\n\nEdit your billing information\n\nYou can edit the following billing information so that it appears on your upcoming invoices:\n\n\n  Company name\n  Address\n  VAT Number\n\n\n\n  Updating your billing information will only apply to future invoices; it is not possible to edit the details of an already created invoice.\n\n\nInvoices\n\nEvery month, we send your invoice in PDF format to the email address linked to your account. You can specify an additionnal email address to which they will be sent, for your accounting department, for example.\n\nOn this page, you will also find a history of your invoices, available in PDF.\n\nTaxes\n\nWe apply taxes in accordance with current regulations. Three scenarios are possible:\n\n\n  your billing address is located outside of Europe: we do not apply VAT.\n  your billing address is located in Europe : we apply a 20% VAT unless you provide your VAT number in your billing details.\n\n\nPayment issues\n\nIn the event of an issue with the charge on your credit card, Stripe will make several debit attempts in the following days. If, despite these attempts, payment remains unsuccessful, our Support team will contact you to assist in finding a solution.\n\n\n  After a certain number of failed attempts, your documentations and accounts may become temporarily inaccessible."
        },
        {
          "id": "help-organizations-create-and-manage-organizations",
          "title": "Create and manage organizations",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/organizations/create-and-manage-organizations/",
          "content": "How to create an organization\n  Add teammates to an organization    \n      Add teammates with SSO\n    \n  \n  Remove members from an organization\n  Change URL/slug\n  Transfer Ownership\n  Delete an organization (and consequences on existing docs)\n\n\nHow to create an organization\n\nTo create an organization, simply reach the account selection in menu in the top bar and click on “Create a new organization”.\n\n\n\nYou can also, from your settings and the Organization section, create a new organization and view the list of those you are a member of.\n\n\n\nYou will then be prompted to specify the organization’s name (visible to all its members and guests) and its slug, which determines its URL (in the format bump.sh/your-organization-slug/).\n\n\n\nAt this stage, you can also choose to move existing documentation and/or hubs to your new organization. These will retain the access settings they had before the migration.\n\nAdd teammates to an organization\n\nFrom the Members section in the settings of an organization, you can add new members, view the list of existing members, modify their roles, and check pending invitations.\n\nTo invite a new member, simply provide their email address and specify the role you wish to assign to them.\n\n\n\nRoles allow you to precisely adjust what a member of your organization can or cannot do. You can change a member’s role at any time.\n\n\n  Please note that only the owner of the organization has access to billing and its options.\n\n\nAdd teammates with SSO\n\nIf your plan allows it, you can add team members via SSO. More information is available in the dedicated section here.\n\n\n  Using SSO does not prevent you from inviting users via manual invitations.\n\n\nRemove members from an organization\n\nStill from the Members section, you can easily remove a member from your organization by clicking on their role to the right of their name and email. The option to delete this member is available at the very end of the list that appears.\n\nChange URL/slug\n\nSlug can be considered as the namespace of your organization, a human-readable URLs pointing to it.\nEverything in your organization, from documentation to hubs, will be below the slug in the URL.\nIt will look like this: https://bump.sh/your-slug/your-documentation.\n\nFrom the settings of your organization, you can quickly change the slug via the dedicated field.\nBump.sh does not automatically redirect to the new URL after changing the slug, so be sure to notify your users of this change.\n\n\n  Modifying the slug changes the URL of all the documentation and Hubs inside your organization.\n\n\nTransfer Ownership\n\nThe owner of an organization must transfer ownership to another admin member if they are leaving it. This operation can be performed by contacting us.\n\nIf you choose to close your Bump.sh account but are still the owner of an organization, a warning message will be displayed.\n\nDelete an organization (and consequences on existing docs)\n\nThis action can be initiated from the organization’s settings, in the danger zone.\n\nDeleting an organization is a significant decision with consequences for your documentation and organization members. It should be made with careful consideration. If you have any doubts or issues, feel free to contact us beforehand.\n\n\n\n\n  Deleting an organization is permanent and irreversible; the documentation and hubs it contains will also be destroyed.\nWe cannot recover content that has been deleted."
        },
        {
          "id": "help-organizations",
          "title": "Organizations",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/organizations/",
          "content": "Organizations are at the heart of your Bump.sh experience. They allow you to access your entire API ecosystem and manage settings with ease.\nWhile perfectly functional for individual projects, organizations are designed to enable your teammates and guests to share this ecosystem together, within the same workspace with specific roles and rules.\n\nYou can switch between them using the dropdown menu, in the top bar.\n\n\n\nNext step: Create your organization"
        },
        {
          "id": "help-organizations-organization-access-management",
          "title": "Organization access management",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/organizations/organization-access-management/",
          "content": "Roles\n  SSO    \n      How to setup SSO\n      Operation with Bump.sh\n    \n  \n\n\nRoles\n\nThe roles allow you to precisely choose which permissions to assign to members of your organization. The details of permissions by role are available below:\n\n\n  \n    \n      Role/Permission\n      Admin\n      Maintainer\n      Viewer\n    \n  \n  \n    \n      Documentation access\n      ✓\n      ✓\n      ✓\n    \n    \n      Hubs access\n      ✓\n      ✓\n      ✓\n    \n    \n      Documentation management\n      ✓\n      ✓\n       \n    \n    \n      Hubs management\n      ✓\n      ✓\n       \n    \n    \n      Invite external guests\n      ✓\n      ✓\n       \n    \n    \n      Organization management\n      ✓\n       \n       \n    \n    \n      Member management\n      ✓\n       \n       \n    \n  \n\n\nA member’s role can be changed at any time through the organization’s list of members. The menu located to the right of each member allows you to change their role.\n\n\n\nSSO\n\nBump.sh supports Single Sign-On through your identity providers, to ease who in your organization can access admin permissions.\n\nOur Single Sign-On feature relies on WorkOS, which supports a wide variety of integrations with third-party Identity and Access Management solutions (e.g. generic SAML, SCIM, OpenID, as well as Auth0, Okta, Keycloak, Azure AD, Google SAML and more).\n\nOnce an SSO connection is set up, a link pointing to a login page is available in the dashboard, allowing you to easily share it.\n\nThis page can be customized with a logo and specific button labels and descriptions (contact us in this latter case).\n\nThe login page will systematically appear when attempting to access via the direct URL of a documentation or a hub behind this SSO, if one is not already authenticated.\n\n\n  SSO is available in our custom plans.\n\n\nHow to setup SSO\n\nTo enable SSO for your organization, contact our support team with the email adress of your team member responsible for the SSO connection.\n\nYou’ll receive a WorkOS invitation link. The setup workflow is guided step-by-step by WorkOS. The configuration step for your identity provider depends on the specific provider you are using.\n\nOnce it’s done, notify our support team so we can finalize the configuration on our end.\n\n\n  By default, all users who get access to Bump.sh via the configured SSO will have the “Viewer” Role/Permission.\n\n\nOperation with Bump.sh\n\nOnce the SSO connection(s) are set up, your private documentation or hubs will only be accessible to authenticated users. You will find the link to the SSO login page in the settings of your organization.\nThis SSO login link redirects to the dashboard.\n\nThe login page will be displayed each time an unauthenticated user attempts to access a private documentation or hub.\n\nThe will always redirect to the initially targeted URL (for example, if a logged-out user uses a URL to access a specific documentation, they will be redirected to that documentation after authentication via SSO)."
        },
        {
          "id": "help-publish-documentation-branching",
          "title": "Branching",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/publish-documentation/branching/",
          "content": "What are branches\n  Create a branch\n  Deploy to a specific branch    \n      Using the Github-Action\n      Using the CLI\n      Using the API\n    \n  \n  Rename or delete a branch\n  Sort branches\n\n\nWhat are branches\n\nWhat we refer to as “Branching” is a solution that allows you to have multiple different definitions of the same API documentation on parallel branches.\n\nBranches can, for example, be used to distinguish between two environments of your API documentation, such as production or development environments. Another example is keeping track of previous versions of an API (e.g., 1.0, 1.1, 2.0, etc.).\n\nYou can create as many branches as necessary, and each of these branches can receive its own deploys and has a distinct changelog from others. You can choose which branch will be displayed and used by default for deployments (when no target branch is designated).\n\nSwitching from one API definition to another in the documentation can be done in an instant via a dropdown menu.\n\n\n\n\n  Branches are available from our Pro plan.\n\n\nCreate a branch\n\nFrom the documentation settings, the Branches section allows you to create branches. Enter the name of the new branch and confirm. You can also create branches directly with our CLI or our API.\n\n\n\nOnce you have created your branch, you can start uploading and deploying your first API definitions using your dashboard, the CLI, or your CI.\n\nDeploy to a specific branch\n\nDeploying an API definition in a branch follows the usual procedure, with an additional step where you specify which branch to add this new definition to.\n\nFrom the documentation settings, in the “Upload” section, select the branch in which to deploy the API definition before confirming the upload.\n\n\n\nUsing the Github-Action\n\nWhen deploying via the Github-Action, you can specify the target branch using the branch: input parameter. If the branch does not exist, it will be created at this stage. Your Bump.sh deployment step will thus need to be changed to:\n\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: DOCUMENTATION_SLUG_OR_ID\n          token: ${{secrets.BUMP_TOKEN}}\n          branch: BRANCH_NAME # New input specifying the branch name\n          file: doc/api-documentation.yml\n\n\n\n  You will need to set a secret variable on your Github repository settings named BUMP_TOKEN with a valid Bump.sh token. This token value can be found in your documentation settings under the “CI deployment” section.\n\n\nUsing the CLI\n\nWhen deploying via the CLI, it is possible to specify the target branch using the --branch &lt;branch-name&gt; parameter. If the branch does not exist, it will be created at this stage.\n\nbump deploy path/to/file.json --doc my-documentation --branch staging\n\n\n\n  You will need to pass your private documentation access token for this command to work. Either with the --token flag or via the BUMP_TOKEN environment variable. This token can be found in your documentation settings under the “CI deployment” page.\n\n\nUsing the API\n\nOur API lets you publish to a specific branch by providing the branch_name request body parameter. However, we recommend using either our CLI or our Github-Action to publish your API documents to Bump.sh.\n\nOur dedicated Branch API endpoints, help to manage the branches of your documentation. You can create a new branch, delete one, list them all, or select one as the default branch.\n\nPlease note that, currently, the API does not support renaming a branch.\n\nRename or delete a branch\n\nThe Branches section allows you to edit the name of a branch, set it as the default (the one that will be displayed when accessing the documentation), or delete it. After changing the branch name, don’t forget to update your CI or CLI deployment settings.\n\n\n\n\n  Deleting a branch is definitive: you will lose the branch, its content and deployment history.\n\n\nSort branches\n\nBy default, branches are sorted alphabetically by name (from A to Z) in the branch selector of an API documentation. You can reverse that order using with “Sorting order” select. Please note that the default branch is always displayed at the top of the list."
        },
        {
          "id": "help-publish-documentation-create-and-manage-documentation",
          "title": "Create and manage documentation",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/publish-documentation/create-and-manage-documentation/",
          "content": "Where can I find my documentation?\n  Create a documentation\n  Categorize docs in a Hub\n  Delete a documentation\n\n\nWhere can I find my documentation?\n\nBy default, accessing your dashboard displays all your documentation.\n\n\n\nCreate a documentation\n\nCreating documentation starts from your dashboard, using the button of the same name. You will then need to provide some essential information:\n\n\n\n\n  \n    \n      Name\n      The name of the documentation as it will appear to those who have access to it.\n    \n    \n      API type\n      Specify if your documentation pertains to a REST API or an Event-Driven API.\n    \n    \n      Deployment method\n      Allows you to choose the deployment type that best suits your workflow. You can modify it at any time.\n    \n    \n      Slug\n      The slug allows you to define the URL of your documentation, in the format https://bump.sh/your-username/your-slug.\n    \n    \n      Documentation Access\n      Documentation can be either public and accessible to everyone or private, visible only to you and invited members.\n    \n    \n      Hub\n      Specify if your doc should be published “stand-alone” or within a Hub\n    \n  \n\n\nThe next step is to deploy the first API document of your documentation, for which you will need your API document: the API definition file.\n\n\n\nThere are several methods for deploying the first and future API definitions of your documentation, which we will detail further in the next section.\n\nCategorize docs in a Hub\n\nHubs are a convenient option to group multiple docs together, like a catalog. Note that adding a doc to a Hub may have it inherit the Hub access management settings, and UI cutomization settings.\n\nAdditionally, it is possible to group docs of a Hub per Category.\n\n\n\nOnce a doc is created, go to the Settings of the doc, under Hubs settings.\n\nYou can set as many categories as needed per doc. Categories must be comma-separated.\n\n\n\n\n  If at least one doc in a Hub has a category set, and that the Hub setting “Group documentation by category” is selected, any doc without a category will not be displayed in the Hub.\n\n\nDelete a documentation\n\nYou can delete documentation at any time from the Settings section of that documentation.\n\nDeleting documentation involves the loss of all uploaded API definitions, the changelog, and the history.\n\n\n  All deletions are final. We cannot restore or recover any portion of the data once it’s deleted."
        },
        {
          "id": "help-publish-documentation-deploy-and-release-management",
          "title": "Deploy and release management",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/publish-documentation/deploy-and-release-management/",
          "content": "What are deployments    \n      Deploy from the dashboard\n      Deploy from the CLI\n      Deploy using the GitHub Action\n    \n  \n  Release management    \n      Manual Release        \n          Manually release a deployment\n        \n      \n      Unrelease an API definition\n    \n  \n\n\nWhat are deployments\n\nA Deployment is the processing of an API document - containing your API definition - uploaded to Bump.sh servers.\n\nFollowing this deployment, the new API definition is published to your API consumers. This is what we call a Release: your API documentation is updated, and if your API has changed since the last deployment, a new entry representing this API change is added to your API changelog.\n\n\n\nBy default, a deployment is automatically released after its processing. However, you can choose to control the release flow by activating the Manual Release setting. In this case, the latest deployments will be enqueued until you decide to release one of them.\n\nYou can also add context (title, description) to any release afterward. This option is available from the page of the deployment, accessible in the list of deployments for your documentation.\n\nDeploy from the dashboard\n\nTo deploy a new API document from the dashboard, you first need to go to the settings of a documentation.\n\nFrom there, you will find the “Deploy new version” button, which will allow you to choose the file to upload and, if the documentation has branches, the branch to deploy it on.\n\n\n\nAfter processing, the new API document will be automatically released after a few moments unless you have enabled Manual Release mode.\n\nDeploy from the CLI\n\nDeploying an API Document can also be done from our CLI using the deploy command. The complete process is available on the dedicated CLI page.\n\nDeploy using the GitHub Action\n\nOur GitHub Action allows you to easily integrate Bump.sh into your projects by adding a workflow file.\n\nRelease management\n\nReleasing a new API definition typically follows a two-step process:\n\n  You upload a new API document through your dashboard, our CLI, GitHub Action or API.\n  This new API document is validated and deployed, becoming the “visible” one. The changelog is also updated, reflecting changes between the current and previous definitions.\n\n\nBy default, these two steps are automatically linked: uploading a new API document results in its release. However, it is possible to dissociate them using the Manual Release mode.\n\nThe Deployments section of your documentation provides access to the history of all API documents uploaded to Bump.sh, including their status (enqueued, deploying, deployed, or errored) and their release status (Released or Not released).\n\n\n\nManual Release\n\n\n  Manual Release is available in our custom plans.\n\n\nThe Manual Release mode is an option that allows you to separate the processing step from the releasing step of your API definition.\n\n\n\nWhen activated, uploading an API document will no longer automatically release it. We continue to prepare this API definition on our side (and you can view a preview and its future changelog entry), but the release step becomes manual. This means you can decide when to make an API definition live for your API users.\n\nThis option allows you to work on future versions of your API without disclosing the changes to your users until the moment you choose.\n\nManually release a deployment\n\nSelect a deployment with the “unreleased” status from the list of your deployments.\n\n\n\nThe release preparation page will appear, allowing you to add context by adding a title or a brief description, for example, to explain the context of the change. These fields support Markdown to make the result more readable.\n\n\n\nUnrelease an API definition\n\n\n  Unrelease a version is available from our Pro plan.\n\n\nIt is possible to “unrelease” any previously released deployment. By clicking on the deployment, you will find the “Unrelease” option under the “…” button.\n\n\n\n\n  Unreleasing a deployment not only rolls back your API documentation to the previously released one. It also automatically reprocesses its changelog to correctly reflect the changes."
        },
        {
          "id": "help-publish-documentation-documentation-access-management",
          "title": "Documentation access management",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/publish-documentation/documentation-access-management/",
          "content": "Public or Private?\n  Private    \n      Members of an organization\n      Invite external guests\n    \n  \n  Public\n\n\nPublic or Private?\n\nBump.sh allows you to choose whether documentation is public or private.\n\nBy “public,” we mean documentation that is accessible to any visitor who has access to it, either by finding it through a direct link or through a search engine.\nPublic documentation is indexed by search engines and can be found by anyone without any restrictions. It is not possible to restrict access to any part of your documentation; it will be fully accessible.\n\nPrivate documentation, on the other hand, is only accessible to users who have been individually invited. There are two types of invited users: those who are part of the same organization and “external” guests.\nThey require user authentication to be accessible and are not indexed by search engines.\n\n\n\nPrivate\n\nMembers of an organization\n\nMembers of an organization have access to all the documentation contained within that organization.\n\nInviting a colleague or teammate to become a member of your organization can be done from the settings of that organization, by entering the email address in the dedicated form.\n\n\n\nInvite external guests\n\n\n  Only admin and maintainer roles can invite external guests.\n\n\nTo invite people from outside of your organization to access a private documentation, click on the Share Access button, which is visible in the settings of that documentation.\n\n\n\nFrom there, you just need to enter the email address of the invited person.\n\n\n\nThey will receive an email inviting them to sign up on Bump.sh. This step is essential to ensure the security of access to your documentation.\n\nPublic\n\nAccess to your public documentation is solely determined by its URL and its discoverability by search engines.\n\nThe slug is the portion of the URL that you can customize but you can also choose to use a custom domain instead:\n\n\n  By default, on Bump.sh, your documentation will be available on this URL https://bump.sh/your-slug/doc/your-documentation\n  With a custom domain, your documentation will be available on https://www.custom.domain.com\n\n\n\n  Any changes to your slug/username will result in a change in the URL of your public documentation, without redirection from our side. Make sure to update the URLs you publish or share with your API users accordingly."
        },
        {
          "id": "help-publish-documentation-external-links",
          "title": "Provide links to external resources",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/publish-documentation/external-links/",
          "content": "Supported specifications: OpenAPI and AsyncAPI\n\n\nExternal resources, such as tutorials or guides that don’t belong inside the API documentation can be linked in your documentation using x-externalLinks.\n\nThe x-externalLinks array can be added at the root of your API document, which will add link(s) to the resource(s) of your choice, displayed in your documentation navigation bar. Each object requires two elements:\n\n  label: the text that will appear on the link,\n  url: the URL the link will point to.\n\n\nx-externalLinks:\n  - label: My first link\n    url: https://my-company.com/my-resource-1\n  - label: My second link\n    url: https://my-company.com/my-resource-2"
        },
        {
          "id": "help-publish-documentation-feedback",
          "title": "Get feedback from users",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/publish-documentation/feedback/",
          "content": "Supported specifications: OpenAPI and AsyncAPI\n\n\nGathering feedback directly from your users allows you to identify areas that need more explanation, thereby continuously improving the quality of your API documentation.\n\nThe x-feedbackLink object can be added in the info section of your API document, which will add a button to your documentation that directly links to the resource (URL) of your choice.\nIt requires two elements:\n\n  label: the text that will appear on the button\n  url: can point to a form, for example, or even an email address (using mailto:).\n\n\ninfo:\n  x-feedbackLink:\n    label: Give feedback\n    url: https://my-company.com/feedback"
        },
        {
          "id": "help-publish-documentation",
          "title": "Publish documentation",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/publish-documentation/",
          "content": "Publishing documentation is the step between your API definition and human-readable documentation.\n\nBump.sh offers various tools to simplify this process.\n\nDeploying your API documents can be done in several ways, through our website, API, CLI, or by integrating with your CI pipeline. You can choose between automatic releases or manual validation to have more control over the published content or share work-in-progress variants with your team.\n\nThis section also covers the concept of branching, which allows you to have multiple versions and/or environements of an API (such as v1.0/v2.0 or production and staging).\n\nFinally, access management details the different levels of accessibility for your documentation and how to secure access to it.\n\nNext step: Create your documentation."
        },
        {
          "id": "help-publish-documentation-seo-geo",
          "title": "SEO & GEO",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/publish-documentation/seo-geo/",
          "content": "Sitemaps\n  Meta titles\n  Meta descriptions\n  GEO (Generative Engine Optimization)    \n      llms.txt for LLM crawlers\n    \n  \n\n\nSitemaps\nTo optimize the indexing of your public documentation, Bump.sh automatically generates a sitemap for each one.\n\nsitemap.xml of a documentation (example truncated for visibility purposes).\n&lt;urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"&gt;\n  &lt;url&gt;\n    &lt;loc&gt;https://bump.sh/bump-examples/doc/train-travel-api&lt;/loc&gt;\n    &lt;lastmod&gt;2025-10-29T15:13:09+01:00&lt;/lastmod&gt;\n  &lt;/url&gt;\n  &lt;url&gt;\n    [...]\n\n\nSimilarly, if you use one or more hubs, they will have their own sitemap_index for all the sitemaps of the documentation they contain.\n\nsitemap_index.xml of a hub (example truncated for visibility purposes).\n&lt;sitemapindex xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\"&gt;\n  &lt;sitemap&gt;\n    &lt;loc&gt;https://demo.bump.sh/doc/external-adyen/sitemap_index.xml&lt;/loc&gt;\n    &lt;lastmod&gt;2020-04-30T21:36:43+02:00&lt;/lastmod&gt;\n  &lt;/sitemap&gt;\n  &lt;sitemap&gt;\n   [...]\n\n\nMeta titles\nThe titles follow the construction below:\n\n[Node Title] - API expanded name ([branch name])\n\n\n  [Node Title] indicates which sections of the documentation the URL points to (e.g., authentication or servers). For the URL pointing to the root of your documentation, this section remains empty.\n  API expanded name uses the name of your documentation followed by API documentation. For example, the documentation Bump becomes Bump API documentation.\n  [branch name] is present only if there is a branch on the documentation.\n\n\nMeta descriptions\nDescriptions are limited to 160 characters and truncated beyond that.\n\nIf a description is provided by info.description in your API definition, it will be used.\nOtherwise, Bump.sh automatically generates a description using the following model:\n\nThis is the documentation for version &lt;code class=\"code-inline\"&gt;#{api_version}&lt;/code&gt; of the API.\nLast update on #{l(api_definition.created_at, format: :date)}\n\n\nGEO (Generative Engine Optimization)\nllms.txt for LLM crawlers\nllms.txt provides context for LLMs, telling crawlers what information can be retrieved behind each page of a documentation. It’s available on both hubs and docs by adding /llms.txt at the end of the URL.\n\nllms.txt of a documentation (example truncated for visibility purposes).\n# Train Travel API\n\n## Description\nAPI for finding and booking train trips across Europe.\n\n## Servers\n- Production: https://api.example.com (Production)\n\n## Topics\n- [Getting started](https://bump.sh/bump-examples/doc/train-travel-api/topic/topic-getting-started.md)\n\n## Endpoints and operations\n\n### [Stations](https://bump.sh/bump-examples/doc/train-travel-api/group/endpoint-stations.md)\n- [Get a list of train stations](https://bump.sh/bump-examples/doc/train-travel-api/operation/operation-get-stations.md)\n- [...]"
        },
        {
          "id": "help-specification-support-asyncapi-support",
          "title": "AsyncAPI Support",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/specification-support/asyncapi-support/",
          "content": "Minimal mandatory fields\n  Currently supported\n  Partially supported: message object payload\n  Partially supported: server security and securitySchemes\n  readOnly and writeOnly properties\n\n\nWe currently support AsyncAPI specification up to version 2.6. If you’re interested in support for version 3.0, feel free to reach out to us directly via email. This pages describes some specificities related to our support for this specification.\n\nMinimal mandatory fields\n\nBump.sh needs to receive at least these fields to generate a Message-Driven documentation\n\n\n  \n    \n      field\n      description\n    \n  \n  \n    \n      asyncapi\n      define which version of the specification you want to use. E.g. \"2.6.0\"\n    \n    \n      info\n      General information about your API\n    \n    \n      info.title\n      The title of the API\n    \n    \n      info.version\n      The version of the API document\n    \n    \n      channels\n      The available channels and messages for the API\n    \n  \n\n\nCurrently supported\n\nBump.sh is able to extract:\n\n\n  root properties (AsyncAPI Object)\n  channels (with automatic grouping according to channel name or tag grouping)\n  operations (subscribe and publish)\n  messages (payload and headers), with examples.\n  bindings (server, operation and message).\n\n\nPartially supported: message object payload\n\nMessage object field payload could be of any type according to the specification, but we only support SchemaObject type.\n\nPartially supported: server security and securitySchemes\n\nAsyncAPI securitySchemes property with these authentication types are supported:\n\n\n  http\n  apiKey\n  httpApiKey\n  oauth2\n  openIdConnect\n\n\nThe following authentication types are not supported:\n\n  X509\n  symmetricEncryption\n  asymmetricEncryption\n  plain\n  scramSha256\n  scramSha512\n  gssapi\n\n\nTo describe these authentication types, please use our custom x-topics property for now.\n\nreadOnly and writeOnly properties\n\nJSON Schema provides the possibility to declare a property as read or write only. Read more in the JSON Schema section of this documentation.\n\nYou can add extra information to your documentation by using Bump.sh custom x-topics.\n\nRead more in the Topics section of this documentation."
        },
        {
          "id": "help-specification-support-doc-badges",
          "title": "Badges",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/specification-support/doc-badges/",
          "content": "State    \n      Example usage\n      Custom color\n    \n  \n  Beta    \n      Example usage\n    \n  \n\n\nState\n\nAdd badges to your endpoints/operations for a quick visual indication of their status.\nSome operations may require specific context to clarify their usage, such as “Technical Preview” or “Soon deprecated.”\n\nx-state is a custom property which can be added inside an operation or a schema to identify it with a custom badge.\nThe x-state property is a string.\n\nExample usage\n\nHere under is an example of an operation and a schema with an x-state.\n\npaths:\n  /diffs:\n    post:\n      description: Create a diff between any two given API definitions\n      x-state: Technical preview # x-state flag at the operation level\n      requestBody:\n        description: The diff creation request object\n        content:\n          application/json:\n            schema:\n              type: object\n              properties:\n                url:\n                  type: string\n                  format: uri\n                  x-state: Still unstable # x-state flag at the schema level\n                  description: |\n                    **Required** if `definition` is not present.\n                    Current definition URL. It should be accessible through HTTP by Bump.sh servers.\n\n\nAdding or removing the x-state property (displaying or removing the badge) is not considered a breaking change in the changelog, as it does not affect the integrity or structure of the API.\n\nImportant: The x-state property does not alter the changelog behavior of the component it is attached to. If the component itself introduces a structural impact on the API (through addition, modification, or removal), the changelog will still display a potential breaking change event.\n\nException: Usage together with the x-beta property.\nRegardless of a component’s structural impact, attaching x-beta will not trigger a breaking change event. If both x-state and x-beta are applied to a component, no breaking change will be generated (the x-beta behavior takes precedence). Only one visual badge will be visible in the documentation: the one with the x-state value.\n\nThe documentation displays custom badges on the operation and property:\n\n\n\nCustom color\n\nThe default color can be overrided. To do so, you can define 2 elements inside the x-state object:\n\n  label: the text that will appear on the badge,\n  color: the color of the text inside the badge. The background color will be generated based on the text’s color, to ensure its readability.\n\n\nx-state:\n  label: \"Experimental\"\n  color: \"#FF6B35\"\n\n\n\n\nBeta\n\nUse the x-beta property inside an operation, a schema or a parameter object to identify it as beta.\nThe x-beta property is a boolean.\n\nA change in a beta component is never identified as a breaking change.\n\nExample usage\n\nHere under is an example of a beta operation, a beta request body and a beta schema attribute.\n\npaths:\n  /diffs:\n    post:\n      description: Create a diff between any two given API definitions\n      x-beta: true # Beta flag at the operation level\n      requestBody:\n        description: The diff creation request object\n        content:\n          application/json:\n            schema:\n              type: object\n              x-beta: true # Beta flag at the top-level schema object\n              properties:\n                url:\n                  type: string\n                  format: uri\n                  x-beta: true # Beta flag at the schema level\n                  description: |\n                    **Required** if `definition` is not present.\n                    Current definition URL. It should be accessible through HTTP by Bump.sh servers.\n\n\nThe documentation displays a “Beta” flag on the components:"
        },
        {
          "id": "help-specification-support-doc-code-samples",
          "title": "Custom code samples",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/specification-support/doc-code-samples/",
          "content": "Example usage\n\n\nEven though we generate a basic cURL code sample by default in your documentation, you might want to customize the example provided and share even more specific language code samples to your API consumers. This is why we added this custom property.\n\nUse the x-codeSamples property inside an Operation OpenAPI object. The x-codeSamples property accepts an array of code sample objects which are defined as such:\n\n\n  \n    \n      Property\n      Type\n      Description\n    \n  \n  \n    \n      lang *\n      String\n      Code sample programming language name.\n    \n    \n      label\n      String\n      A label which will be used as a title in the code sample bloc. Defaults to the lang value if not provided.\n    \n    \n      source *\n      String\n      The source code sample content.\n    \n  \n\n\n\n  Bump.sh relies on the Highlight.js lib to color your code. If your language is not supported, the source code will be displayed anyway, just without color syntax.\n\n\nExample usage\n\nThe following screen capture shows the rendering of adding two code samples cURL and Ruby to your Operation.\n\n\n\nThis is done by adding the following x-codeSamples array to your API definition:\n\npaths:\n  /users:\n    get:\n      summary: Retrieve a user\n      operationId: getUserPath\n      responses: [...]\n      parameters: [...]\n      x-codeSamples:\n        - lang: cURL\n          label: Custom cURL\n          source: |\n            curl \\\n              --user \"name:password\" \\\n              --request GET \\\n              --url 'https://api.example.com/v1/users' \\\n              --header 'Accept: application/json'\n        - lang: ruby\n          label: Ruby library\n          source: |\n            require \"http\"\n             \n            request = HTTP\n              .basic_auth(:user =&gt; \"name\", :pass =&gt; \"password\")\n              .headers(:accept =&gt; \"application/json\")\n             \n            response = request.get(\"https://api.example.com/v1/users\")\n            if response.status.success?\n              # Work with the response.body\n            else\n              # Handle error cases\n            end"
        },
        {
          "id": "help-specification-support-extensions",
          "title": "Extensions",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/specification-support/extensions/",
          "content": "Add topics to your documentation (x-topics)\n  Custom code sample examples (x-codeSamples)\n  Add badges to your endpoints/operations (x-state)\n  Expose your beta features (x-beta)\n  Get feedback from users (x-feedbackLink)\n  Add links to external resources (x-externalLinks)\n  Add custom meta tags (x-metaTags)\n  Change tags display name (x-displayName)\n  Define role requirements (x-rolesRequirements)\n\n\nThese additional properties are not specified by the OpenAPI or the AsyncAPI specifications but can help you customize your documentation content. All those properties start with the x- naming convention to be identified as “eXternal” from the OpenAPI or AsyncAPI specification.\n\nAdd topics to your documentation (x-topics)\n\nThis vendor-specific property we created helps to add more context paragraphs in your generated documentation. Find out more in our dedicated section.\n\nCustom code sample examples (x-codeSamples)\n\n\n  This vendor extension is only available for OpenAPI documents for now\n\n\nWe added a custom property, not supported by OpenAPI, so you can add your own code samples in one or more programming languages to your documentation. Find out more in our dedicated section.\n\nAdd badges to your endpoints/operations (x-state)\n\nThis custom property can be added to an operation/endpoint to display a contextual badge in the documentation.\nMore information on its usage is available in the dedicated section.\n\nExpose your beta features (x-beta)\n\nThis custom property allows you to identify some components of your documentation as beta. Find out more in our dedicated section.\n\nGet feedback from users (x-feedbackLink)\n\nThis custom property allows you to add a button to your documentation that enables you to collect feedback from your users. Find out more in our dedicated section.\n\nAdd links to external resources (x-externalLinks)\n\nThis custom property lets you add links to the navigation bar of your documentation, redirecting users to external resources. Find out more in our dedicated section.\n\nAdd custom meta tags (x-metaTags)\n\nThis custom property lets you add custom meta tags in the &lt;head&gt; tag of your documentation pages. Find out more in our dedicated section.\n\nChange tags display name (x-displayName)\n\nThis custom property lets you change the display name of your OpenAPI tags. Find out more in our dedicated section.\n\nDefine role requirements (x-rolesRequirements)\n\nThis custom property specifies which roles or permissions are required to access an endpoint. Find out more in our dedicated section."
        },
        {
          "id": "help-specification-support",
          "title": "Specification Support",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/specification-support/",
          "content": "Bump.sh supports the most popular specifications for REST and Event-Driven APIs.\n\n\n  OpenAPI Specification\n  AsyncAPI Specification\n\n\nWe do plan to support more specifications in the future. If there is one specifically you’re looking for, feel free to contact us!"
        },
        {
          "id": "help-specification-support-json-schema",
          "title": "JSON Schema support",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/specification-support/json-schema/",
          "content": "readOnly and writeOnly properties\n  Enumerated or constant values\n\n\nBoth OpenAPI and AsyncAPI specifications use JSON Schema to define input and output data types, identified as a Schema Object:\nMost of the time, a Schema Object is defined with keyword schema, and each child property can also be defined as a Schema object (as per definition of the JSON Schema specification).\n\n\"schema\": { // this property is a Schema Object\n  \"type\": \"object\",\n  \"properties\": {\n    \"hello\": {\n      // this property named 'hello' is defined by Schema Object\n      \"type\": \"string\"\n    },\n    \"world\": {\n      // this property named 'world' is also defined by Schema Object\n      \"type\": \"string\"\n    }\n  }\n}\n\n\nHere a documentation we love about JSON Schema\n\nThus, JSON schema is supported by Bump.sh, for both AsyncAPI ond OpenAPI Specification.\n\nreadOnly and writeOnly properties\n\nJSON Schema provides the possibility to declare a property as read or write only, with boolean fields writeOnly and readOnly (cf JSON Schema documentation).\n\nThus, it becomes easy to use the same Schema Object in different contexts, for example as seen below:\n\n\"schema\": {\n  \"type\": \"object\",\n  \"properties\": {\n    \"password\": {\n      \"type\": \"string\",\n      \"format\": \"password\",\n      \"writeOnly\": true\n    },\n    \"created_at\": {\n      \"type\": \"string\",\n      \"format\": \"date-time\",\n      \"readOnly\": true\n    }\n  }\n}\n\n\n\n  \n    \n      writeOnly properties are hidden when they belong to a subscribe operation in AsyncAPI or a response in OpenAPI.\n    \n    \n      readOnly properties are hidden when they belong to a publish operation in AsyncAPI or a request in OpenAPI.\n    \n  \n\n\n\n  Not displaying writeOnly properties in subscribe operations and readOnly properties in publish operations allows the use of the same Schema Object everywhere it is needed, without documenting confusing/irrelevant properties in the wrong context.\n\n\nEnumerated or constant values\n\nIt’s frequent to have to restrict possible value(s) for a given property, and two keywords\nare available in JSON schema to do this:\n\nWhen several values are possible, good practice is to use keyword enum,\nas mentioned in Enumerated values documentation.\n\nKeyword enum is used, it has to declare an array of all possible values.\nIn your documentation, you’ll see a sentence:\n\n  Values are foo and bar.\n\n\nWhen only one value is possible, instead of enum with one-item array,\nyou should consider keyword const, used to declare a single allowed value,\nas mentioned in Constant values documentation.\n\nIn this case, property description in your documentation will look like that:\n\n  Value is 42.\n\n\n\n  enum and const?\n\n  When both enum and const fields are provided, allowed values is not easy to deduce.\n\n  foo, bar, 42?\n\n  In this case, only const field is extracted, and you should be warned about in your deployment logs."
        },
        {
          "id": "help-specification-support-meta-tags",
          "title": "Custom meta tags",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/specification-support/meta-tags/",
          "content": "Example\n\n\nAdd custom meta tags to the head of your documentation pages with the x-metaTags extension. It can be used, for example, to improve your own global search with more precise filtering through facets, thanks to customized meta tags retrieved by your internal crawler.\n\nx-metaTags can be added at different levels:\n\n  root,\n  topic,\n  operation,\n  webhook,\n  message,\n  binding,\n  tag (when the grouping mode is set to “Group by tag”).\n\n\nEach object inside the x-metaTags array requires two elements:\n\n  name : the value that will be given to the name attribute,\n  content : the value that will be given to the content attribute.\n\n\n\n  Tags added to the root of the API definition file will be added to every page of the documentation. Tags added to any other level will only be added to the corresponding pages.\n\n\nExample\nHere’s an example of x-metaTags added to the root of the API definition and to an operation.\n\nAdding the x-metaTags in the API definition file\nx-metaTags:\n  - name: First root tag\n    content: First root tag content\n  - name: Second root tag\n    content: Second root tag content\npaths:\n  /user:\n    post:\n      x-metaTags:\n      - name: Operation tag\n        content: Operation tag content\n\n\nImpact on the &lt;head&gt; if the loaded documentation page is the root of the API documentation\n&lt;head&gt;\n  &lt;meta name=\"First root tag\" content=\"First root tag content\" /&gt;\n  &lt;meta name=\"Second root tag\" content=\"Second root tag content\" /&gt;\n&lt;/head&gt;\n\n\nImpact on the &lt;head&gt; if the loaded documentation page is the POST /user operation\n&lt;head&gt;\n  &lt;meta name=\"First root tag\" content=\"First root tag content\" /&gt;\n  &lt;meta name=\"Second root tag\" content=\"Second root tag content\" /&gt;\n  &lt;meta name=\"Operation tag\" content=\"Operation tag content\" /&gt;\n&lt;/head&gt;"
        },
        {
          "id": "help-specification-support-multiple-servers",
          "title": "Multiple Servers",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/specification-support/multiple-servers/",
          "content": "Both REST and Event-Driven APIs may run on different servers (production, staging, sandbox).\n\nAnd both OpenAPI and AsyncAPI provide field servers, where more than one server can be defined.\n\nWith multiple servers support, it’s now possible to render these different servers in documentation.\n\nSetting multiple servers\n\nPublishing the list of all of your servers in your documentation on Bump.sh is optional. In some cases, you might need to expose only the first server of the list.\n\nTo show the list of servers on your documentation, activate the setting from the Settings tab of your documentation administration panel:\n\n\n\nRender multiple servers\n\nWhen this setting is activated and your specification contains several servers, your documentation will display a list of all servers,\naccessible from this url: ${documentation_url}/servers\n\nBy default, the first server is used to generate all curl examples on your documentation.\n\nReaders of the documentation can select a different server from this selection component:\n\n\n\nFor REST API documentation, the url of selected server is used to generate curl examples.\nFor Event-Driven API documentation, protocol and protocol versions are updated depending on the selected server.\n\nWhen sharing a link to the documentation as a reader, the server_id query parameter will be passed with the URL, so that the recipient will see the exact same information as the person who shared it.\n\n\n  For REST APIs, alternative servers can be defined for every OpenAPI Operation Object:\n\n  Operation objects can include a servers field, which is an array of servers for this specific operation.\n\n  \n    If an alternative server object is specified at the Path Item Object or Root level, it will be overridden by this value.\n  \n\n  It means this operation has its own and dedicated url to receive requests.\n\n  Thus, of course, curl examples for these operations are not concerned by the selection of multiple servers: only this alternative server is displayed."
        },
        {
          "id": "help-specification-support-openapi-support",
          "title": "OpenAPI support",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/specification-support/openapi-support/",
          "content": "Minimal mandatory fields\n  Partially supported: securitySchemes (V3) / securityDefinitions (V2)\n  Partially supported: XML generated examples\n  readOnly and writeOnly properties\n  Webhooks support\n  Overlays support\n  OpenAPI 3.2: current support\n\n\nWe support all major versions from Swagger (OpenAPI v2), OpenAPI v3/3.1, and partially OpenAPI v3.2. This pages describes some specificities related to our support for this specification.\n\nMinimal mandatory fields\n\nBump.sh needs to receive at least these fields to generate a REST documentation\n\n\n  \n    \n      field\n      description\n    \n  \n  \n    \n      openapi or swagger\n      define which version of the specification you want to use. Use the swagger key for v2 and openapi for the v3+. E.g. openapi: \"3.2.0\".\n    \n    \n      info\n      General information about your API\n    \n    \n      info.title\n      The title of the API\n    \n    \n      info.version\n      The version of the API document\n    \n  \n\n\nPartially supported: securitySchemes (V3) / securityDefinitions (V2)\n\nWe support OpenAPI securitySchemes property (securityDefinitions with openAPI v2) with these authentication type values:\n\n\n  http\n  apiKey\n  oauth2\n  openIdConnect\n\n\nWe do not support mutualTLS. To describe a mutualTLS authentication method, please use the x-topics property for now.\n\nPartially supported: XML generated examples\n\nWe currently generate request or response examples in both JSON and\nXML format if none are provided in your Schema object\ndefinitions. However we don’t yet support the xml: attribute on\nSchema objects.\n\nreadOnly and writeOnly properties\n\nJSON Schema provides the possibility to declare a property as read or write only. Read more in the JSON Schema section of this documentation.\n\nWebhooks support\n\nYou can use the webhooks field (introduced in OpenAPI 3.1) to define the API webhook payloads. Please read to the dedicated documentation page for more information.\n\nOverlays support\n\nThe Overlay specification of OpenAPI makes it possible to modify the content of an OpenAPI document by adding a layer on top of it. That layer helps adding, removing or changing some or all of the content of the original file. Please read to the dedicated documentation page for more information.\n\nOpenAPI 3.2: current support\n\nWe are progressively rolling out our OpenAPI 3.2 support. This is what’s currently supported:\n\n  Method: QUERY method, in both the documentation and the API Explorer.\n  Server object: name property.\n  Tag object: summary property, behaving like our existing x-displayName vendor extension.\n  Response object: summary property.\n  Security schemes: deprecated property."
        },
        {
          "id": "help-specification-support-openapi-support-webhooks",
          "title": "Webhooks",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/specification-support/openapi-support/webhooks/",
          "content": "One of the greatest new features provided by OpenAPI 3.1 is the support of webhooks. Indeed, a OpenAPI 3.1 documentation may include paths and / or webhooks, when paths were required for previous version.\n\nEvery webhook has a required keyName, and some operations. If we follow this example, provided for OpenAPI 3.1 by OpenAPI Initiative:\n\nwebhooks:\n  # Each webhook needs a name\n  newPet:\n    # This is a Path Item Object, the only difference is that the request is initiated by the API provider\n    post:\n      description: A new pet arrived, let's come and discover it IRL.\n      requestBody:\n        description: Information about a new pet in the system\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/Pet\"\n      responses:\n        \"200\":\n          description: Return a 200 status to indicate that the data was received successfully\n\n\nHere, there is a single webhook whom keyName is newPet, and a POST operation. If such a documentation was generated by Bump (see live documentation):\n\n\n\n\n  Webhook’s name is deduced from the keyName.\n  Webhook’s operation name is extracted from field summary.\n  As for endpoints, webhook has body parameters, responses… and some curl and request payloadexamples are generated.\n\n\nFurthermore, webhooks are fully compatible with UI customization How to group operations?\n\nBy adding some tags to your webhooks, you can re-organize how webhooks are named, sorted, and how webhooks operations are sorted."
        },
        {
          "id": "help-specification-support-openapi-support-x-display-name",
          "title": "Change tags display name",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/specification-support/openapi-support/x-display-name/",
          "content": "Supported specification: OpenAPI. \nNote that OpenAPI 3.2 now supports summary on tags, behaving the same way as x-displayName.\n\n\nThe x-displayName property allows for custom tag names. When added, it replaces the default name value in the documentation.\n\ntags:\n  - name: myFirstTag\n    x-displayName: Branches\n  - name: mySecondTag\n    x-displayName: Diffs"
        },
        {
          "id": "help-specification-support-overlays",
          "title": "Overlays",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/specification-support/overlays/",
          "content": "Note: The Overlays Specification of OpenAPI is currently in\nbeta. It is sufficiently stable for experimentation,\nhowever please be advised that it may change depending on the\ncommunity feedback.\n\n\nThe Overlay specification makes it possible to modify the content of an API definition file by adding a layer on top of it. That layer helps adding, removing or changing some or all of the content of the original definition.\n\nFrom your API description, and with an Overlay definition file, you can merge the two into a new definition file that you will then deploy to Bump.sh.\n\nThe Bump.sh CLI lets you merge the files.\n\nThe resulting file can then be deployed and released to Bump.sh as per your usual workflow, whether manually, via our CLI, API or GitHub Action.\n\nDiscover our Augmenting Generated OpenAPI Documents with Filters &amp; Overlays guide to learn more about this new feature."
        },
        {
          "id": "help-specification-support-polymorphism",
          "title": "Polymorphism",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/specification-support/polymorphism/",
          "content": "Discriminator\n\n\nBump.sh fully supports polymorphism for OpenAPI and AsyncAPI, commonly called anyOf, oneOf and allOf.\nMore details on these combinators can be found on each specification documentation:\n\n\n  OpenAPI\n  AsyncAPI\n\n\nTo ease readability, we strongly recommend to give titles to your alternatives: it will help to identify and search them, specifically if you use this feature extensively.\n\nHere’s an example of polymorphism in action:\n\n\n\nDiscriminator\n\nBoth OpenAPI and AsyncAPI specifications provide support of a discriminator field,\nto easily identify which schema is used.\n\nIn our generated API documentation, this discriminator property is identified\nwith a specific flag, and allowed value is explicit.\n\nIn example provided above:\n\n  type is the discriminator property between alternatives ‘Human’ or ‘Pet’. Value has to be either human or pet.\n  kind is the discriminator property between alternatives ‘Dog’ or ‘Cat’. Value has to be either 🐕 or 🐈."
        },
        {
          "id": "help-specification-support-references",
          "title": "References",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/specification-support/references/",
          "content": "What is a reference?\n  The two types of references    \n      Internal references\n      External references\n    \n  \n  How to deploy a specification with external references\n\n\nReferences are the JSON Schema system to not repeat common parts of your API contracts. Bump supports internal references and external ones.\n\nWhat is a reference?\n\nA reference can be either a pointer to another part of your current specification or a pointer to an external file or URL. This lets you re-use part of a specification instead of duplicating it across your file(s).\n\nA reference is always described as follows in YAML:\n\n$ref: path/to/reference\n\n\nYou should note that OpenAPI and AsyncAPI specifications don’t allow you to use references anywhere. However, Bump will resolve all the references found in your files, even if you didn’t respect your specification.\n\nThe two types of references\n\nInternal references\n\nUse this when you want to point to another part of your current file. Internal references always start with #/.\n\nExample:\n\npaths:\n  /ping:\n    description: Test your API status\n    responses:\n      200:\n        $ref: \"#/responses/pong\"\n\n\nInternal references are always expanded by Bump. If a reference points on an empty path, the reference is just ignored.\n\nExternal references\n\nUsing external references is particularly useful as it can point to external resources like full definitions, components, models, etc… External references let you reuse your components across multiple projects without duplicating your specification code.\n\nYou can find two kinds of external reference locations supported by Bump:\n\n\n  URI: a resource hosted online and accessible through HTTP\n  File system path: a resource located on the same file system as your definition\n\n\n# URI\n$ref: https://example.com/api/specification.yml\n\n# File system path\n$ref: ./models/user.yml\n\n\n\n  Bump supports absolute and relative paths for file system references.\n\n\nAn external reference can also point to a subpart of the resource by adding a relative path after the absolute location:\n\n# URI\n$ref: https://example.com/api.yml#/models/user\n\n# File system path\n$ref: ./models.yml#/user\n\n\n\n  You can use recursive references if needed but we limit up to 5 recursive reference maximum.\n\n\nHow to deploy a specification with external references\n\nBump does support external references from any channel, so you can deploy a specification including them through the web app, the CLI, our GitHub action, or our API.\n\n\n  Some limitations apply when using the web application though. Only references pointing to resources accessible by our servers will be resolved. This means that file system paths and protected URI will be ignored.\n\n  If you need to deploy a specification using this kind of external references, please use our CLI or our GitHub Action"
        },
        {
          "id": "help-specification-support-roles-requirements",
          "title": "Role requirements",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/specification-support/roles-requirements/",
          "content": "Example usage    \n      Using an array of strings\n      Using a single string\n    \n  \n  Impact on the changelog\n\n\nSpecify which roles or permissions are required to access an API endpoint with x-rolesRequirements. Combined with security schemes, this extension helps your API users understand both what authentication method they need and what access levels or roles their credentials must have.\n\nAdd the x-rolesRequirements property to any OpenAPI/AsyncAPI operation or OpenAPI webhook. The property accepts either a string or an array of strings.\n\nExample usage\n\nUsing an array of strings\n\npaths:\n  /clusters:\n    post:\n      summary: Create a new cluster\n      x-rolesRequirements:\n        - Organization owner\n        - Product owner\n      # ... rest of operation\n\n\n\n\nUsing a single string\n\npaths:\n  /clusters/{id}:\n    get:\n      summary: Get cluster details\n      x-rolesRequirements: \"Cluster privileges: read\"\n      # ... rest of operation\n\n\n\n\nImpact on the changelog\n\nRoles requirements updates are not visible in the changelog."
        },
        {
          "id": "help-support-and-sla",
          "title": "Support and Service Level Agreement (SLA)",
          "collection": {
            "label": "help",
            "name": "Help"
          },
          "categories": "",
          "tags": "",
          "url": "/help/support-and-sla/",
          "content": "Support\n  Service Level Agreement\n  Monitoring    \n      Status page\n      Status notifications\n    \n  \n\n\nSupport\n\nWe believe in human interactions. Support is ensured only by the Bump.sh team, not by any chatbot.\n\nYou can at anytime reach out to us via support@bump.sh, or via the chat icon which may appear at the bottom right hand side of some of the screens (mostly on our marketing site, the product documentation pages, and your administration dashboard - never on user-facing documentation pages).\n\nWe take pride in offering exceptional support to any of our users, no matter which plan they’re on.\n\nService Level Agreement\n\nAs we understand the importance of having docs always up-and-running, available for your users to understand and adopt your APIs, we put all reasonable efforts in maintaining an above 99.9% availability.\n\nFor customers of our custom plans, we commit to that level. If we fall short, we can grant service credits. The credit is based on how much availability dropped below the target and gets applied to a future invoice. It’s a simple mechanism, just meant to give some peace of mind around reliability.\n\nMonitoring\n\nSince we want to offer utmost transparency as of the status of our services, we offer a status page, and the ability to subscribe to any events that would be reported through that page.\n\nStatus page\n\nOur status page is available at https://status.bump.sh/.\n\nIt reports on any active event, current and past. Statuses and metrics are detailed for each service (API portal, deployment queue, …).\n\nMetrics are available in real time from our monitoring systems.\n\nIf any event occurs, our team is warned directly and will most of the time be working on it (and potentiall have fixed it) by the time you might realize it.\n\nIn rare cases, for events that may last longer, or for any scheduled maintenances that may impact our services, we will post announcements via that page.\n\nStatus notifications\n\n“Pull” mechanisms are commonly used, but “push” notifications can be essential in certain cases. You can subscribe to event notifications through the status page to stay informed in real time.\n\nSuch notifications may be delivered via:\n\n\n  Email\n  Slack\n  RSS\n  SMS\n  Webhook\n\n\nWebhook configuration\n\nOur status page is powered by Uptime.com, and so is the notification system. The complete documentation for setting up a webhook is available on their support site.\n\nIn short:\n\n  Set up a handler, accessible by a public URL;\n  Use this URL under “Subscribe via Webhook” on https://status.bump.sh;\n  Any event will trigger a POST to that URL.\n\n\nThe application/json payload will look like this example:\n\n{\n\"name\": \"Test incident\",\n\"description\": \"This is an incident update\",\n\"starts_at\": \"2024-02-02T20:40:54.398000Z\",\n\"ends_at\": null,\n\"duration\": 125.416398,\n\"incident_type_display\": \"Incident\",\n\"updates\": [\n     {\n         \"created_at\": \"2024-02-02T20:40:54.398000Z\",\n         \"updated_at\": \"2024-02-02T20:40:54.398000Z\",\n         \"description\": \"This is an incident update\",\n         \"incident_state\": \"identified\",\n         \"incident_state_display\": \"Identified\"\n     }\n],\n\"affected_components\": [],\n\"created_at\": \"2024-02-02T20:40:54.642672Z\",\n\"incident_state\": \"identified\",\n\"incident_type\": \"INCIDENT\",\n\"url\": \"https://url-to-get-incident-details-via-api\",\n\"manage_url\": \"https://url-to-manage-sp\",\n\"unsubscribe_url\": \"https://url-to-unsubscribe\"\n}"
        },
        {
          "id": "product-updates-2020-02-20-introducing-our-github-action",
          "title": "Introducing our GitHub action",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New feature",
          "url": "/product-updates/2020/02/20/introducing-our-github-action/",
          "content": "Deploying your API documentation has never been so simple if you are using GitHub. We have just released our GitHub action, which lets you super easily control when and how you want to deploy your doc.\n\nAll you have to do is configuring your workflow:\n\nsteps:\n  - name: Checkout\n    uses: actions/checkout@v2\n  - name: Deploy API documentation\n    uses: bump-sh/github-action@0.1\n    with:\n      id: &lt;BUMP_DOC_ID&gt;\n      token: &lt;BUMP_DOC_TOKEN&gt;\n      file: doc/api-documentation.yml\n\n\nYou can see it in action on our workflow examples 😎"
        },
        {
          "id": "product-updates-2020-04-24-introducing-hubs",
          "title": "Your whole ecosystem on a single page",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New feature",
          "url": "/product-updates/2020/04/24/introducing-hubs/",
          "content": "Today, we are super excited to release a long-awaited feature: Hubs. You can now create your APIs catalogs, directly from Bump.\n\n\n\nHighlighted features:\n\n  custom domain support\n  documentations can be grouped by tag\n  default settings at the hub level, applied to all its documentations\n  auto-creation of hub documentations from your CI\n  and obviously, OpenAPI and AsyncAPI support\n\n\nHubs are available with our Business plan.  Learn more on our help center."
        },
        {
          "id": "product-updates-2020-06-12-enhanced-examples",
          "title": "Documentation examples for lazy people",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2020/06/12/enhanced-examples/",
          "content": "What’s the first thing you look at when exploring a new tool? If you’re as lazy as I am, it’s probably the examples. As we think they are one of the most important parts of your documentation, we gave them the attention they deserve.\n\nFirst, we have improved the way we auto-generate examples when missing. We now generate them based on the format of the attribute, for the following formats: email, url, date, date-time and UUID.\n\n\n\nFor requests with a body, we now display the whole body example, just under the cURL command example.\n\nAnd last but not least, copying an example has never been easier: we have added a copy button for cURL and request body examples:"
        },
        {
          "id": "product-updates-2020-07-02-better-change-summaries",
          "title": "Better change summaries",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2020/07/02/better-change-summaries/",
          "content": "We have enhanced the way we summarize API changes. We now list any changes, at all levels (endpoints, requests, responses, attributes &amp; headers) in the API changelogs and notifications we send.\n\nHere an example of a previous summary:\n\nEndpoint updated: Versions\nEndpoint updated: Validations\n\n\nWhich now becomes:\n\nUpdated: POST /docs/{:id}/versions\n  Body attribute added: specification\n  Response added: 200\n  Responses removed: 201, default\nUpdated: POST /docs/{:id}/validations\n  Body attribute added: specification\n  Response removed: default\n\n\nIt is now active for all new deployments. We’ll be recalculating the summaries of previous deployments to make your API changelogs clearer soon."
        },
        {
          "id": "product-updates-2020-11-12-add-a-favicon-to-your-documentation",
          "title": "Add a favicon to your documentation",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2020/11/12/add-a-favicon-to-your-documentation/",
          "content": "Until now, for documentation having a customised logo, the same one was used as a favicon.\n\nFrom now on, you can use a specific one, to brighten up your documentation more than ever!\n\nHere is how Bump’s documentation looks like now:\n\n\n\nAnd icing on the cake: it works for your hub too!\n\nHappy enhancement!"
        },
        {
          "id": "product-updates-2020-11-18-show-verbs-in-navigation",
          "title": "Show verbs in navigation",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2020/11/18/show-verbs-in-navigation/",
          "content": "Displaying operations in an endpoint or a channel is sometimes not enough. \nYou can now configure your documentations to show operation verbs in navigation.\n\nTo enable it, go to your documentation settings “Customize UI” tab, then select “Groups and operations with verbs” in the “Navigation” input."
        },
        {
          "id": "product-updates-2020-11-22-depreciation-support",
          "title": "Depreciation Support",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2020/11/22/depreciation-support/",
          "content": "We now identify deprecated elements in both OpenApi and AsyncApi specifications, and highlight them in your documentations.\n\nWe wanted to release it fast, as it was an important missing feature, but this is just the beginning: we have a lot of ideas on how we can enhance deprecation support that we will implement in the upcoming weeks.\n\nWant to share some use cases with us? Pick a slot on Anthony’s calendar, he will be super glad to hear from you."
        },
        {
          "id": "product-updates-2020-12-01-custom-domain-for-everyone",
          "title": "Custom domain for everyone",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2020/12/01/custom-domain-for-everyone/",
          "content": "You can now configure your custom domain directly from the app. No more manual email to us, just create your CNAME record, fill the form, and voilà.\n\nWe also have good news for you: we’ve updated our pricing plans to allow you to set up custom domains on free plans. One would say it’s Christmas, before Christmas. Enjoy!"
        },
        {
          "id": "product-updates-2021-01-10-customize-operation-url",
          "title": "Customize operation URL",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2021/01/10/customize-operation-url/",
          "content": "OpenAPI provides field servers on OperationObject, as an alternative url to service an operation.\n\n  If an alternative server object is specified at the Path Item Object or Root level, it will be overridden by this value. cf OpenAPI-Specification\n\n\nFrom now on, Operations servers are supported to customize your operations examples (in definition and curl example)."
        },
        {
          "id": "product-updates-2021-02-04-hello-openapi-webhooks",
          "title": "Hello, OpenAPI webhooks",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2021/02/04/hello-openapi-webhooks/",
          "content": "OpenAPI 3.1 has been released today. Some important changes are coming with this new version. Above all, the greatest improvement is the support of webhooks, at the same first level than paths.\n\nWe are glad to announce today that webhooks are now supported by Bump, as you can see in this live example.\n\nWe hope you’ll enjoy this new feature, don’t hesitate to have a look to our technical documentation if necessary."
        },
        {
          "id": "product-updates-2021-03-16-global-support-of-external-references",
          "title": "Global support of external references",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2021/03/16/global-support-of-external-references/",
          "content": "As your API grows, your specification becomes more and more complex. At some point, splitting it up into multiple files with $ref references pointing to these external resources can help a lot.\n\nWe have just released full external references support  by updating:\n\n  our web app\n  our CLI (version 0.7 is out)\n  our GitHub action (version 0.2 is out)\n\n\nNow, no matter the channel you use to deploy a specification, Bump will resolve all references (when possible) and import their content into your documentation.\n\nWant to learn more? Have a look at [our documentation]/help/specification-support/references/)."
        },
        {
          "id": "product-updates-2021-05-20-documentation-in-your-pocket",
          "title": "Documentations in your pocket",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2021/05/20/documentation-in-your-pocket/",
          "content": "We now offer a better mobile experience for your documentation if you happen to need it on-the-go. You will also have a navigation menu that allows to access the changelog page.\n\nMore changes are coming, stay tuned!"
        },
        {
          "id": "product-updates-2021-05-23-ease-your-sight-with-dark-mode",
          "title": "Ease your sight with dark mode ☾",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2021/05/23/ease-your-sight-with-dark-mode/",
          "content": "Today we introduce the dark mode to your documentations! Depending on your system settings, your page will appear in a light mode (default) or in a darker scheme.\n\nYou’ll also be able to toggle to light or dark mode regarding your own preferences.\n\nWe hope you will enjoy the new experience. 👻"
        },
        {
          "id": "product-updates-2021-05-29-new-command-line-interface-era",
          "title": "New Command Line Interface era",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2021/05/29/new-command-line-interface-era/",
          "content": "Goodbye to our legacy ruby gem, and hello to our new node-based CLI 👋.\n\nWe are releasing a new Command Line Interface which is iso-feature with the old CLI. There is one additional improvement though: we now support recursive external references ($ref keyword in your API specifications), with mixed filesystem or URL paths. You can now refactor your API definitions in small chunks, re-use parts of it, and separate concerns between endpoints, models or nested-objects in peace.\n\nThis new CLI package is written in Typescript which will help us to publish a stable &amp; type safe tool. On top of that -and especially with the oclif framework- we are now able to publish universal packages for multiple OSes &amp; architectures so everybody can enjoy Bump via command line interface.\n\nGet it while it’s hot\n\nnpm install -g bump-cli\n\n\nor download a package directly from the latest Github release assets.\n\nEnjoy the commands\n\n\n  bump preview to build as many API documentation preview as you want.\n  \n    bump deploy --dry-run to validate your future API documentation deployment.\n\n    (⚠️ This is a breaking change compared to our old CLI’s and replaces the old bump validate command)\n  \n  bump deploy to deploy your latest API documentation changes."
        },
        {
          "id": "product-updates-2021-06-09-nested-properties-get-a-make-up",
          "title": "Nested properties get a make up 🎀",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2021/06/09/nested-properties-get-a-make-up/",
          "content": "Feedbacks are important to us and we do our best to make you feel heard.\nWe know that accessing nested properties is a pain point for many of you so we tried to make the navigation clearer and the selected element will now stand out more.\n\nWe’d love to hear from you and see if these changes suit your needs so hit us up with your comments whether you love it or hate it!"
        },
        {
          "id": "product-updates-2021-07-02-bump-diff-from-your-cli",
          "title": "Bump diff from your Command Line Interface or your Continuous Integration platform",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2021/07/02/bump-diff-from-your-cli/",
          "content": "Our new bump diff command has landed in our CLI. \nMake sure to upgrade your bump-cli package to at least v2.1.0 to test it.\n\nNow, straight from your CLI, you are able to describe the changes made in your contract:\n\n&gt; bump diff --doc users-account api-specification.yaml\nUpdated: GET /account\n  Response modified: 200\n    Body attribute added: ticket_ids\nAdded: GET /tickets/{ticket_id}\n\n\nThe new command will output a quick summary of what has changed between your latest deployed API contract and the file you have changed locally.\n\nIf you use Github Actions to launch your automation workflows, we have some more good news for you: we now offer a stable bump-sh/github-action@v1 action. And this latest release includes automatic API contract changelog pushed as a comment for each pull request.\n\nCheck our help page to get you started with team collaboration on API design."
        },
        {
          "id": "product-updates-2021-07-13-download-source-from-your-documentation",
          "title": "Download source from your documentation",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2021/07/13/download-source-from-your-documentation/",
          "content": "We’ve made it easy for API consumers to download the source of an API documentation.\n\nWhether you want to get your hands on a JSON or YAML format, for OpenAPI or AsyncAPI, you’re now just a click away."
        },
        {
          "id": "product-updates-2021-07-18-receive-api-changelog-in-your-mailbox",
          "title": "Receive API changelog in your mailbox 🔔",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2021/07/18/receive-api-changelog-in-your-mailbox/",
          "content": "In API documentation, changelogs are important, and your API consumers need to stay up-to-date on recent changes.\n\nIt’s now possible for them to subscribe by email to your API changelog: they will receive a summary of the changelog every week in their mailbox!\n\nWant to see it in action on Bump API documentation? See it live\n\nDo not hesitate to invite your API consumers to subscribe to your API updates (available at http://{your-doc-url}/changes)\n\nNever miss an API change again (now, it’s in your mailbox in case you missed it 😉)"
        },
        {
          "id": "product-updates-2021-09-07-markdown-support",
          "title": "Markdown support",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2021/09/07/markdown-support/",
          "content": "As markdown is a standard and a common way to bring more readability into your documentation, we have made some improvements on markdown support and bring new features to help you create fast and beautiful API documentations.\n\nBump now supports common Markdown syntax, language color syntax highlighting, and information call-outs. Last but not least, Markdown can be included inside your contract file or as an external reference using dedicated Markdown files.\n\nCheck our help page to start taking advantage of markdown in your API documentation."
        },
        {
          "id": "product-updates-2021-09-11-share-and-highlight-anything-inside-your-api-documentation",
          "title": "Share and highlight anything inside your API documentation",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2021/09/11/share-and-highlight-anything-inside-your-api-documentation/",
          "content": "To give your users the best API onboarding, \nit often helps to be able to share specific pieces of information.\n\nThanks to this new feature, you can now share and highlight anything in your API documentation with just a click. The selected element will be highlighted following your color theme.\n\n\n\nGive it a try: https://developers.bump.sh/operation/operation-post-previews"
        },
        {
          "id": "product-updates-2021-09-16-bindings-support-for-asyncapi",
          "title": "Bindings support for AsyncAPI",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2021/09/16/bindings-support-for-asyncapi/",
          "content": "AsyncAPI users, you’ll be happy to know that Bump now supports bindings.\n\nAs AsyncAPI allows many protocols, which is really nice, bindings are a useful and necessary mechanism to define protocol-specific information. Bump now displays protocol information defined at server, operation, and message levels, as part of an ongoing work for a better understanding of API documentations.\n\nHere’s an example: https://bump.sh/hub/examples/doc/asyncapi-user-signup#protocol-information"
        },
        {
          "id": "product-updates-2021-09-20-rss-feed-for-your-changelog",
          "title": "RSS feed for your changelog",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2021/09/20/rss-feed-for-your-changelog/",
          "content": "In the age of information overload, getting access to information that matters can be quite challenging and that’s exactly the purpose of RSS.\n\nFrom now, get notified of the API changes through RSS in addition to the existing email.\n\nGive it a try with the Bump RSS feed in your favorite RSS reader."
        },
        {
          "id": "product-updates-2021-09-21-changelog-makeover",
          "title": "Changelog makeover 💄",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2021/09/21/changelog-makeover/",
          "content": "We redesigned the whole changelog page in order to bring you a better understanding of changes history of your API.\n\nHere’s an example: https://developers.bump.sh/changes"
        },
        {
          "id": "product-updates-2021-10-03-automatic-breaking-change-detection",
          "title": "Automatic breaking change detection",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2021/10/03/automatic-breaking-change-detection/",
          "content": "If some changes are more important than others, it is especially true for your API.\n\nFrom now on, we automatically detect if a new version of your API includes some breaking changes. It will be visible in the changelog page, in the changelog email or the brand new RSS feed… but also during your API design phase while opening a pull request thanks to our Github Action.\n\n\n\nWith Bump, you will never miss a breaking change again."
        },
        {
          "id": "product-updates-2021-10-12-bump-live-preview",
          "title": "Bump live preview",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2021/10/12/bump-live-preview/",
          "content": "A new --live flag was added to the bump preview command.\n\nThis gives you the ability to stay focused on your API definition file (OpenAPI or AsyncAPI file) while seeing your Bump preview documentation page being updated as you change the file.\n\nNo need for a specific text editor, or any extension, just use the bump CLI to preview your documentation live.\n\n\n\nLearn more about live preview on our help page."
        },
        {
          "id": "product-updates-2021-11-08-multiple-examples-support",
          "title": "Multiple examples support",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2021/11/08/multiple-examples-support/",
          "content": "Give your API users a personalized experience and a better understanding of your API while reading your documentation by including multiple examples to your API definition.\n\nBump now supports displaying multiple examples which are supported by both OpenAPI and AsyncAPI specifications."
        },
        {
          "id": "product-updates-2021-11-14-bump-diff-navigation",
          "title": "Bump diff navigation",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2021/11/14/bump-diff-navigation/",
          "content": "✨  Quickly identify what has changed in your API ✨\n\nJump from change to change! No need to scroll anymore to see the latest changes in your documentation diff."
        },
        {
          "id": "product-updates-2022-01-15-changelog-update",
          "title": "Changelog update",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2022/01/15/changelog-update/",
          "content": "The API changelog page has been redesigned to give you a clearer view of what has changed inside your API, it is the groundwork of having fine-grained changelog details 🕵️\n\nHope you will enjoy it!\n\nSee it in action on our own API documentation\n\n• Bonus point:\n\nIf you use our github-action, the diff comments have also been updated. Check our latest release for details: https://github.com/bump-sh/github-action/releases/tag/v1.1.0"
        },
        {
          "id": "product-updates-2022-03-10-organization-support",
          "title": "Organization support",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2022/03/10/organization-support/",
          "content": "Building the best APIs can be a considerable amount of work and you may need your teammates to help. But you definitely do not want one single shared account for that.\n\nFreshly out of beta, the awaited Organizations feature helps you with that. We did not reinvent the wheel but wanted to make it as simple but powerful as possible.\n\n\n\nYou can easily customize access with 3 different roles: admin, maintainer, and viewers.\nAdmins can invite and manage access, maintainers can manage hubs and docs, guests are limited to read-only.\n\nAvailable from the Startup plan, we hope it will help teams work efficiently and smoothly on their APIs. You can find out more about Organizations in our Help Center!\n\nThis update was also the opportunity to ship some enhancements for the dashboard.\n\nWe can’t wait to hear about what you think of it so feel free to reach out!"
        },
        {
          "id": "product-updates-2022-03-20-documentation-access",
          "title": "Documentation Access",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2022/03/20/documentation-access/",
          "content": "Following our recently released Organization feature, the new Documentation Access Management will help control who has access to which documentation or hubs.\n\nThree access levels are available: public, private (for your organization only) and protected (limited, password protection).\n\nFind more in our Help Center!"
        },
        {
          "id": "product-updates-2022-04-02-webhooks",
          "title": "Webhooks",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2022/04/02/webhooks/",
          "content": "Today, we’re introducing Webhooks integration, as a new way to add Bump to your existing workflow.\n\nSet them up from your dashboard and receive notifications when events on your API structure are detected on your documentation.\n\nFind more in our Help Center!"
        },
        {
          "id": "product-updates-2022-05-07-colors",
          "title": "COLORS",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2022/05/07/colors/",
          "content": "You already have deployed your API documentation but feel something’s missing? You couldn’t be more right.\n\nIntroducing one of the biggest updates to Bump yet: UI Color customization. You read that right. You can now change the primary color of your documentation to make it shine more than ever. Or make it match your logo/company brand!\n\nWe even added a short preview to help you find the best tone and ensure that you’ll keep in mind how it will look in dark mode too.\n\nTry it out from your Dashboard &gt; Customize UI and share with us your best results!"
        },
        {
          "id": "product-updates-2022-06-04-multiple-types-for-properties-copie",
          "title": "Multiple types for properties",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2022/06/04/multiple-types-for-properties-copie/",
          "content": "Here is an update that was released some time ago but out of the radar: we now display all available types (even nullable) on any properties in your OpenAPI or AsyncAPI definition.\n\nYou’ll now see these correctly displayed properties in your documentation. Multiple types may look like a slight improvement but will add clarification to your team and community.\n\nAnd we have an example to show how great it is (thanks to Meilisearch’s documentation)."
        },
        {
          "id": "product-updates-2022-06-13-search",
          "title": "Search!",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2022/06/13/search/",
          "content": "Here comes a long-expected new feature: the Search Bar. We know how time-consuming it can be to scroll through long API documentation to find what you are looking for: the Search Bar solves this in a blink of an eye.\n\nOur Search engine can look in your documentations and even your hubs on your APIs structures: endpoints, objects, properties, webhooks, etc… And to make it more comfortable, we sort them by types (and it looks great, seriously).\n\nWe are already working on two limitations: you cannot search in your spec history and we are lacking advanced filters.\n\nDiscoverability is an important topic on our end. We want to help teams and communities deep dive into their API ecosystem, making them easier to find and use. We will have a lot more to announce soon and we are preparing a small blog post on this feature’s making-of.\n\nWe hope you’ll enjoy the Search Bar: feel free to reach out if you have any feedback or suggestion!\n\nP.S. Search will be activated on your documentation/hub after the next deploy but please contact us if you need it before this as we can manually activate it."
        },
        {
          "id": "product-updates-2022-07-08-branching",
          "title": "Branching",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2022/07/08/branching/",
          "content": "Introducing Branching, a great way to host several versions of an API Specification on the same documentation.\n\nIf your API has several versions you would like to maintain or if you are working between two environments, branching will make a difference and limit the number of documentation in your hub.\n\nCreate as many branches as you need, upload files accordingly and find the versions of your API anytime in your documentation, through a small selection menu.\n\nDefine which branch should be chosen by default, add or delete branches, keep everything up-to-date and share with your ecosystem the best API experience possible."
        },
        {
          "id": "product-updates-2022-09-05-automated-image-generation",
          "title": "Automated Image Generation",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2022/09/05/automated-image-generation/",
          "content": "After working so hard on your API and its documentation, the moment has come to share it with the world.\nWe recently released a new social network feature, that helps you create beautiful visuals pointing to your API.\n\nYou can now upload your own image or leave the work to us to generate a nice one. Sharing your documentation link on social networks will now display your own visual, making it more appealing.\n\nFind out more in our Help Center!"
        },
        {
          "id": "product-updates-2022-11-14-polymorphism",
          "title": "Polymorphism",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2022/11/14/polymorphism/",
          "content": "They have many names: schema alternatives, discriminator, inheritance… but you probably know them as oneOf, anyOf and allOf.\n\nBump now fully supports polymorphism for AsyncAPI and OpenAPI. You can already upload your latest API specification file to check it out. Or see it in action with our demo documentation of the feature!"
        },
        {
          "id": "product-updates-2022-12-07-improved-access-management",
          "title": "Improved Access Management",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2022/12/07/improved-access-management/",
          "content": "Access management has been reshaped, thanks to extended tests and user feedback.\n\nInviting external customers and partners to access internal APIs is now possible directly from the dashboard.\n\nFrom the dedicated section in your hub/documentation settings, you can quickly modify who has access to what. Open it to teammates of your organization, or invite external partners by email directly.\n\nPassword-protected level will stay available for a limited amount of time. More info can be found in our help center."
        },
        {
          "id": "product-updates-2023-01-27-enhanced-specifications-support",
          "title": "Enhanced Specifications Support",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2023/01/27/enhanced-specifications-support/",
          "content": "Over the past weeks, we have considerably enhanced our support of the specifications.\n\nminLength, maxLength and pattern\n\nWherever you need to document restrictions on string fields, OpenAPI and AsyncAPI specifications rely on JSON Schema to support this.\nminLength and maxLength  are meant to constrain the length of a string.\npattern  restrict the string to a specific Regular Expression (regex).\n\nEmail addresses, IBANs and ZIP/Postal codes could be great examples of these features.\n\n\n\nreadOnly  and  writeOnly  properties\n\nJSON Schema allows defining a property as readOnly  or  writeOnly.\nMany examples can easily be imagined when used with AsyncAPI or OpenAPI : readOnly  timestamp, writeOnly  password, etc…\n\nOur Help Center shares more details on how Bump.sh works with this feature and a few examples."
        },
        {
          "id": "product-updates-2023-06-03-lightning-fast-api-documentation-rendering",
          "title": "Lightning-fast API documentation rendering",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2023/06/03/lightning-fast-api-documentation-rendering/",
          "content": "Fast-loading page is a must these days, both for human users and search engines.\n\nThanks to the new rendering engine, which has already been released to 100% of our users, navigating your API documentation is now a breeze. It provides an optimal experience for all API consumers, whatever the size of the documentation (yes, any size).\n\nPerformance is significantly better: the first interactive content is as fast as the blink of an eye, which also means there’s no more blocking time when you first navigate the API documentation."
        },
        {
          "id": "product-updates-2023-06-14-bump-cli-2-7-0",
          "title": "Bump.sh CLI v2.7.0",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2023/06/14/bump-cli-2-7-0/",
          "content": "Multi-file Deployment\n\nDeploying multiple files on your Hub has never been this effortless. You can now provide a directory path to deploy all files within a directory to your Bump.sh hub.\n\nFor a more detailed walkthrough on how this works, check out the feature in action in our latest release notes.\n\nLive Preview\n\nUsing the Live Preview feature  bump preview --live my-definition-api.yml now creates an immediate preview without waiting for the first change on the target file.\n\nOther improvements\n\n\n  Keeping up with the latest version of the CLI is now easier thanks to a new warning in case of outdated version usage.\n  Significant refactor of the deployment-related code to accommodate the new “Multiple files deploy” feature.\n  CLI upgraded to TypeScript 4.5 and performed some dependency upgrades."
        },
        {
          "id": "product-updates-2023-06-16-api-branch-management",
          "title": "Managing Branches from your API",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New, API",
          "url": "/product-updates/2023/06/16/api-branch-management/",
          "content": "Branch Management is now available from the Bump.sh API, making your API evolution workflows a whole lot smoother as an easy and effective way to manage your versions and optimizes how you deliver ephemeral or major ones, at hand (at least within our API).\n\nLet’s explore how these new features simplify your API workflow\n\n\n  Branch creation: Create a new branch effortlessly with a single API call, eliminating manual work.\n  List of Branches: Obtain an overview of all API branches, facilitating efficient management and organization.\n  Set Default Branch: Easily choose and showcase a default branch for up-to-date API documentation.\n  Delete Branch: Remove unnecessary branches to maintain a clean and organized development environment."
        },
        {
          "id": "product-updates-2023-08-01-custom-code-samples",
          "title": "Custom code samples",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2023/08/01/custom-code-samples/",
          "content": "We now support the x-codeSamples extension that let you set language-specific code samples you expect your users to work with.\n\nExamples are often the first stop for developers in any API documentation. This extension allows you to set along a programming language, its custom code samples, for a more seamless developer experience when it comes to testing an API.\n\nEither supercharge your API documentation by setting language-specific code examples or keep things simple as the auto-generated cURL examples remain. See more about custom code sample in the docs."
        },
        {
          "id": "product-updates-2023-08-09-changelog-layout",
          "title": "Improved Changelog",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2023/08/09/changelog-layout/",
          "content": "The changelog is a crucial part of your API ecosystem, providing a clear view of what’s new, changed, fixed or removed.\nWe’ve redesigned it to make it cleaner, clearer and more efficient while maintaining the ability for anyone to stay aware of updates, one click away.\n\nCheck how it looks on the Bump.sh API doc: it is absolutely stunning."
        },
        {
          "id": "product-updates-2023-08-30-change-comparison",
          "title": "Comparison between (any) API changes",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2023/08/30/change-comparison/",
          "content": "APIs evolve constantly, each iteration creating changes, breaking ones or not. API consumers need to keep track of them or could need to upgrade from a several behind version.\n\nWe have worked on a new feature that allows to compare any versions of an API with another, accessible from its changelog. Changes are instantly highlighted, and if you need to easily share the result with your teammates, we got you covered: use the generated link or even better, try to press Y key!\n\nCome and test this new feature directly on Bump.sh API documentation.\n\nImprovements and fixes\n\n\n  API documentation: Sidebar navigation has received UI enhancements.\n  Dashboard: History became Deployments along UI enhancements and a new status filter (spoiler alert).\n  Darkmode: All icons have been updated to be accessible on both light and darkmode.\n  API documentation: Ordering issue (operation, responses, parameters, etc.) has now been solved and respects the given specification."
        },
        {
          "id": "product-updates-2023-09-28-manual-release",
          "title": "Introducing Manual Release Mode",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2023/09/28/manual-release/",
          "content": "Gain greater control over your API Documentation deployments by using the Manual Release Mode for your API documentation.\n\nGrouped changes: Multiple changes are sometimes part of a single feature or improvement. Using this mode allows for these changes to be grouped together under a single changelog entry.\n\nTiming: Keep merging your PRs and following your API changes on a daily basis, but control when you and your team communicate these changes to you API users.\n\nQuality Assurance: Add an extra layer of QA and collaboration before a change is officially published.\n\nLearn more in our Release Management documentation.\n\nImprovements and fixes\n\n\n  Change Comparison: Route simplification to enhance UX.\n  Specification: Multiple server support.\n  API Documentation: Fix navigation accessibility on darkmode.\n  CI Deployment settings: Token are now hidden by default.\n  Dashboard: Better deployment warning label."
        },
        {
          "id": "product-updates-2023-10-05-editable-release",
          "title": "Editable Change Release",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2023/10/05/editable-release/",
          "content": "As part of our feature series on Release Management, making a change release editable was the obvious “go the extra mile” effort, while allowing you to further manipulate a change to be announced.\n\nCustom title and description allow you to provide additional context that may not be apparent in an automatic diff summary. You can now explain why a change was made, not just what changed.\n\nDetailed release notes can serve as a form of documentation, helping both current and future developers understand the history and evolution of an API.\n\nLearn more in our Release Management documentation."
        },
        {
          "id": "product-updates-2023-11-02-changelog-diff",
          "title": "Changelog Diff",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2023/11/02/changelog-diff/",
          "content": "The lack of information between changes was repeatedly highlighted in our discussions with some of you. As the information contained in a diff was not obvious, it was sometimes difficult to visualize critical updates, which led us to take action.\n\nWith the new Changelog Diff, you get a clearer overview of EVERY change. Instead of a simple “this item has changed”, you can now see specific details, allowing API users to know when to act and what they need to find out.\n\nWe’ve also overhauled the visual aspect of a diff. The hierarchy of information is clearer and more intuitive, as is the order of change statuses, which is now better ordered.\n\nNow available to see on your API, or see it in action on Bump.sh API Changelog →"
        },
        {
          "id": "product-updates-2023-11-15-seach-engine-performances-enhancement",
          "title": "Search Engine Performances Enhancement",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2023/11/15/seach-engine-performances-enhancement/",
          "content": "Since its release in the summer of 2022, our Search Engine has quickly become very popular for your searches across your documentation and hubs. However, we didn’t plan to stop at this initial iteration.\nWe have completely reworked the Search Engine algorithm to make searches even faster, especially across your hubs.\n\nOverall, the search speed has been increased by a factor of 10. And in certain situations, particularly for organizations with numerous hubs and documentations, the search is now up to 20 times faster."
        },
        {
          "id": "product-updates-2023-11-25-x-beta",
          "title": "Beta property",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2023/11/25/x-beta/",
          "content": "When some items in your API may become deprecated, others are introduced and still in the stabilization process but ready to be tested by your users. For the latter, we have created the custom property x-beta.\nIt allows you to mark an operation, a schema, or a parameter object in your documentation as being in beta. We recommend its usage to experienced users, as a change on a beta item will never be considered as breaking by our automated changelog generation.\n\nFind out more in our dedicated article."
        },
        {
          "id": "product-updates-2023-12-15-help-center-remastered",
          "title": "Help Center Remastered",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2023/12/15/help-center-remastered/",
          "content": "Writing beautiful documentation is something that we deeply care about at Bump.sh. Therefore, we have spent the last few months completely rewriting our product documentation, including a structure overhaul, to address as many questions as possible and to assist you in better understanding and leveraging Bump.sh.\n\nIn it, we cover the core concepts of API documentation, solutions to help you integrate Bump.sh into your tools and workflows, and all the features, from customizing your documentation to deployment options.\n\nOur help center is open source: we warmly welcome any suggestions or corrections. You can consult our contribution guidelines to learn more about this."
        },
        {
          "id": "product-updates-2024-01-06-api-keys",
          "title": "API keys",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2024/01/06/api-keys/",
          "content": "The API key, also known as a token, grants access to our API and allows you to integrate it into your continuous integration workflows. So far, it was working at the scale of a specific hub or a documentation.\n\nWe are evolving API keys so that they can now operate at the organization global level and everything it contains.\nThis change aims not only to greatly simplify the use of our API but also lays the groundwork for upcoming features, such as user management or the creation of documentation/hubs/etc…\n\nFor this migration of API keys, we made sure not to touch the existing ones to allow your existing workflows to continue functioning without any issues."
        },
        {
          "id": "product-updates-2024-02-19-new-dashboard-experience",
          "title": "New dashboard experience",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2024/02/19/new-dashboard-experience/",
          "content": "It’s been a big project that we’ve poured a lot of energy and even more love into, and our new dashboard is finally available to everyone.\nIt’s become a true home for your API ecosystem and everything that happens within it. From creating a hub to exploring your documentation, everything has been redesigned to offer you the best possible experience.\n\nWe’ve also put the finishing touches on another major change: the removal of individual spaces. To discuss this, let’s take a step back.\n\nIn its early days, Bump.sh was designed for individual developers but quickly became collaborative. The vast majority of our users now work in teams.\nUntil now, each user had their own personal space and could create and/or join organizations. In practice, many personal workspaces remained empty and unused, there was often confusion between the two, and the experience wasn’t the most comfortable.\n\nTo put it briefly, all documentation and hubs now belong to an organization.\nFor our users, nothing changes or hardly anything: your existing hubs and documentation have been migrated to an organization bearing your username, and your URLs still work.\nWe have naturally adjusted our Free plan to not impact our users under this plan: it now includes an organization (yours) with all its APIs publicly accessible as before."
        },
        {
          "id": "product-updates-2024-04-05-love-cycle",
          "title": "Love Cycle - April 24",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2024/04/05/love-cycle/",
          "content": "We have a tradition of scheduling what we call Love Cycles between our development cycles: times dedicated to improving Bump.sh, responding to specific user requests that we find relevant, and providing a general polish.\n\nOur last Love Cycle has recently ended, and we wanted to share with you all the small changes we’ve made. If you have a feature in mind or a need, don’t hesitate to share your feedback with us at hello (at) bump.sh!\n\nImprovements:\n\n\n  Overlay Support: Overlay is a new feature (beta) of OpenAPI that allows applying a layer over an API document without modifying it, to adapt to a specific context. Now supported by Bump.sh, we also added to our CLI a new command. Feel free to read our recent guide on the topic!\n  Examples will now display correctly regardless of the chosen format. Bonus for those in XML which will also display the appropriate highlight.\n  [OpenAPI] Improved support for externalValue which now displays a link to the designated resource.\n  When using the GitHub Action for a diff, we now add a link to the preview of the result directly in the PR.\n\n\nQuality of Life:\n\n  We have improved the appearance of highlights during navigation on documentation, to make them more visible and comfortable.\n  Navigation has been improved for logged-in users, by adding buttons for quick return to the Dashboard.\n  From now on, the timestamp of your deploys and other actions will be based on your browser’s timezone instead of GMT+1 by default.\n  We have added and modified some warning messages to make them more explicit.\n\n\n\n\nFixes:\n\n  We resolved the issue with discriminator values not always mapping correctly in certain specific cases. Special thanks to Phil Sturgeon for pointing it out and having a thorough discussion about discriminators with the team.\n  The generation of some API keys was not always going smoothly, but this is now fixed.\n  [AsyncAPI] The binding support is now properly applied at the channel level."
        },
        {
          "id": "product-updates-2024-06-03-apis-json",
          "title": "APIs.json support",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2024/06/03/apis-json/",
          "content": "Looking for a comprehensive file listing all the APIs inside your hub? We’ve got you covered!\n\nWe support APIs.json, to automatically generate a JSON file out of your hub content.\n\nTo access the JSON, simply add /apis.json at the end of your hub URL. It contains the description, type, version, documentation URL, and more about each API.\n\nFor example, it can be used to:\n\n  Check in an automatic CI context that a precise list of APIs is published on your hub\n  Add your APIs to an API search engine (e.g. APIs.io)\n\n\nWe have also published a detailed guide to help you get started with APIs.json.\n\nYou can try it out on our demo hub here →"
        },
        {
          "id": "product-updates-2024-06-10-changelog-entry-page",
          "title": "Changelog entry page",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2024/06/10/changelog-entry-page/",
          "content": "We have recently improved the changelog to make it easier to share updates to your API documentation.\n\nIn addition to the continuous deployment flow of the changelog, you can now click on each entry to view an individual page for a specific change. This option allows you to easily share a link directly pointing to the changes you don’t want your API consumers to miss, whether they are members of your organization or partners.\n\nOur API documentation allows you to easily test the rendering of these new pages, as shown with this example.\n\nWe have many more updates coming to the changelog this year, and we can’t wait to show them to you."
        },
        {
          "id": "product-updates-2024-07-10-love-cycle",
          "title": "Love Cycle - July 24",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2024/07/10/love-cycle/",
          "content": "We have been busy improving Bump.sh through small enhancements, specific user requests, while globally polishing the product. We call these moments “Love Cycles”.\n\nHere is the list of every small change we made.\n\nAs always, we love to hear about any features you would like to see added to the product. Send us a message at hello (at) bump.sh!\n\nImprovements:\n\n  When you are subscribed to an API changelog, the link inside the email notifying about a new change now redirects to the specific change page.\n  Single change: we worked on the page to remove duplicated information, and put the focus the deployment date and change id, that can both be useful depending on who reads it. We also put some love on the mobile version.\n\n\nQuality of life:\n\n  API keys, that let you configure automatic deployments or use the CLI can be set at the organization level, at a Hub level, or at a doc level. By default, you can always use an API key from a parent resource (for instance, the API key of the Hub or the Org that the doc belongs to). We made it clearer in the UI by displaying the parent resource API key in the Automatic Deployment settings page. It still is possible to create a dedicated key for each resource.\n  Deployments page: we removed the tabs allowing to filter between All, Released and Not Released deployments to improve the page’s readability. It is still possible to filter deployments by State (Enqueued, Deployiong, Deployed, Errored).\n\n\nFixes:\n\n  Webhooks in documentation now display the generic URL https://webhook.example.com.\n  We fixed a visual bug on selects, that could make the user think that multiple items were active.\n  We fixed a bug that made the title and description of an more recent change disappear when an user unreleased an older deployment.\n  Timestamps are now displayed using the browser’s timezone."
        },
        {
          "id": "product-updates-2024-07-25-access-management",
          "title": "Access management",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2024/07/25/access-management/",
          "content": "It’s been a while since we added the first pillar of our access management system. It gave documentation maintainers a way to grant access to internal users (“members”) and external users (“guests”).\n\nAs our customers’ documentation reader bases grew, the way we handled user administration needed to evolve.\n\nSay hello to the improved access management page, designed to easily manage members and guests from a single place. \nThe new table shows more users per page and offer sorting options. You can also see which resources guests have access to directly from this table and revoke their accesses when needed.\n\nOn another note, we know that SSO can be very handy for companies with a lot of users. By working closely with our clients, we improved our SSO support to allow two SSO connections for the same organization:\n\n  One for for internal users (“members”),\n  One for external users (“guests”).\n\n\nThe SSO login page can now be customized with your company’s logo, providing a seamless experience for your users. Learn more about SSO here →\n\nWe hope these improvements will make it easier for you to manage access to the platform.\n\nAs always, we warmly welcome your feedback!"
        },
        {
          "id": "product-updates-2024-08-10-feedbacks-gathering",
          "title": "Get feedback from users",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2024/08/10/feedbacks-gathering/",
          "content": "Because feedback from real-life users is the best way to improve your documentation, our last update introduces a new “Give feedback” button.\n\nThe x-feedbackLink object can be added directly in the info object of your API definition file.\n\nBy letting you redirect this button to any link, we want to give you the freedom to choose the type of feedback you want:\n\n  A pre-filled issue in your GitHub repository using a template,\n  A nicely designed form,\n  An email to the support team, …\n\n\nCheck our dedicated help page →\n\n\n\nAs always, don’t hesitate to let us know what you think of this feature! You can even use our “feedback” button on our dashboard!"
        },
        {
          "id": "product-updates-2025-01-09-api-explorer",
          "title": "Introducing the API Explorer",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2025/01/09/api-explorer/",
          "content": "We are happy to announce the launch of our API Explorer, our clients’ most requested feature.\n\nSending a first request is the last mile of an API discovery. As setting up an API client or using cURL in a CLI can be an endeavour for API users, we made things easier, by integrating the API Explorer into your documentation portal.\n\nWith the API Explorer, enable your users to go from API discovery to API usage, in a single place.\n\nSend requests, right next to the API documentation\nIt can be difficult for API consumers to understand the complete behavior of an API, that’s one of the reasons we make documentation. To ease the API’s discoverability in the Explorer, the request form is automatically generated with the right field types, based on the OpenAPI definition. No more Lorem Ipsum inside a date picker.\n\nReadability is key in a context of discovery. A dedicated view for the API Explorer next to the documentation means focusing on what’s important: the customer’s request, and the API’s response, while keeping the documentation just one click away.\n\n\n\nDeep support of OpenAPI\nYour API definition file is a contract between you and your users. That’s why we went all-in on supporting the OpenAPI specification in our API Explorer. From simple properties such as strings and booleans, to more complex ones such as arrays of objects or oneOf/anyOf, try any sort of request, based on any API design.\n\nRequest sharing\nPre-defining and sharing a request can be really valuable for API maintainers, to:\n\n  onboard new clients with a ready-to-play getting started,\n  facilitate customer support,\n  detect inconsistencies between the API definition and the live API,\n  help debugging, …\n\n\nAPI Explorer users have a one-click sharing option, providing a unique link that will autofill the request form with the shared values. Let people reproduce the exact behavior you wanted to show them.\n\nOnly the request is shared (the authentication method and the response are not).\n\nWhen your API evolves, Bump.sh automatically detects if a shared request has become obsolete, alerts your users of possible field changes, and provides them with a diff summary comparing the two versions.\n\n\n\nSecure by design\nTesting requests sometimes involves using sensitive or confidential information. That’s why we built it safe from the ground up: we will never see any information about your APIs. Requests, execution and sharing are handled 100% client side.\n\nFor APIs that don’t allow CORS requests, we built a proxy that makes it work without any specific configuration on your side. This proxy called cors-toujours is 100% open source, and hosted on a different server with no logs or data access.\n\nEverything remains between the API consumer and the API itself.\n\nHelp your users discover your APIs with mock servers\nOur freshly announced partnership with Microcks gives a hint about our next focus: we are looking into ways of enhancing the discovery experience by streamlining mocking capabilities.\n\nA mocking server creates a playground to deeply play with the API, without the hassle of setting up a demo server or impacting the production server.\n\nInterested in setting up mock servers for your APIs? Contact us!\n\nWhat it means for us\nWe are really proud of the first version of the API Explorer. It’s a new pillar in our vision: to create a next generation documentation platform that brings multiple tools together with one goal: to provide the smoothest API adoption possible for API consumers.\n\nThe API Explorer is currently in beta. You can request access here.\n\nAnd last, but not least, you can discover it now with our demo API!\n\nLive demo\nYoan Gross, our Product Manager/Designer, made a demo to explain our vision and approach."
        },
        {
          "id": "product-updates-2025-01-29-mermaid-support",
          "title": "Mermaid support",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2025/01/29/mermaid-support/",
          "content": "Sometimes, the best way to explain something about your API is to do it visually. That’s why we’re announcing Mermaid support!\n\nDiagrams and charts are powerful tools, that can be used to share updates on your API development roadmap or give key information on data processing flow, for example.\n\nDiscover the full list of diagrams and charts supported by Mermaid here.\n\nTo generate one, add a code block declared as mermaid inside a description or content property:\n\n  get:\n    description: |\n      ```mermaid\n      erDiagram\n        CUSTOMER }|..|{ DELIVERY-ADDRESS : has\n        CUSTOMER ||--o{ ORDER : places\n        CUSTOMER ||--o{ INVOICE : \"liable for\"\n        DELIVERY-ADDRESS ||--o{ ORDER : receives\n        INVOICE ||--|{ ORDER : covers\n        ORDER ||--|{ ORDER-ITEM : includes\n        PRODUCT-CATEGORY ||--|{ PRODUCT : contains\n        PRODUCT ||--o{ ORDER-ITEM : \"ordered in\"\n      ```\n\nThis example would render the following diagram (see it live):\n\n\n\nThe support of Mermaid is part of our global Markdown support. Learn more about it in our help center.\n\nAs always, don’t hesitate to reach out if you have feedback about this feature!"
        },
        {
          "id": "product-updates-2025-02-13-api-explorer-auth",
          "title": "API Explorer - Authentication support",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2025/02/13/api-explorer-auth/",
          "content": "Authentication is now supported for APIs providing authentication methods.\n\nWe currently support:\n\n  HTTP: Bearer/Basic,\n  API keys: Header/Query string/Cookie,\n  OAuth2: basic support through a header token field.\n\n\nUsers only have to define it once to use it on any of your API operations that require it.\n\nWant to try the API Explorer? You can request access here.\n\nThe API Explorer is still in beta. We warmly welcome your feedback."
        },
        {
          "id": "product-updates-2025-03-07-badges",
          "title": "Introducing state badges for operations and properties",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2025/03/07/badges/",
          "content": "Operations and properties go through multiple states during their lifetime, from unstable to beta or release candidate.  This information can be crucial for your users. And more importantly, it can be completely specific to your API lifecycle. That’s why we are introducing a new property named x-state.\n\nThis custom property is a string that can be added inside an operation or a schema to identify it with a custom badge:\n\n    paths:\n    /diffs:\n        post:\n        description: Create a diff between any two given API definitions\n        x-state: Technical preview # x-state flag at the operation level\n        requestBody:\n            description: The diff creation request object\n            content:\n            application/json:\n                schema:\n                type: object\n                properties:\n                    url:\n                    type: string\n                    format: uri\n                    x-state: Still unstable # x-state flag at the schema level\n                    description: |\n                        **Required** if `definition` is not present.\n                        Current definition URL. It should be accessible through HTTP by Bump.sh servers.\n\nIn this example, we added an x-state property to the operation itself, and one of its properties.\n\n\nLearn more about it in our help center\n\nAs always, don’t hesitate to reach out if you have any feedback regarding this new feature."
        },
        {
          "id": "product-updates-2025-04-10-love-cycle-april-25",
          "title": "Love cycle - April 25",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2025/04/10/love-cycle-april-25/",
          "content": "Every now and then between larger product cycles, the whole team gets together to polish the product by working on small enhancements and user requests, in a time called “Love cycle”.\n\nHere’s a list of what’s been worked on.\n\nImprovements:\n\n  You can now easily list all your organization’s hubs using the new GET /hubs endpoint in our API,\n  Our GitHub Action has been updated and now supports overlays,\n  Bump.sh CLI:\n    \n      A message is now displayed at every document update, so you know the CLI is running and updating the preview properly,\n      When using bump overlay, we now display a message if an overlay doesn’t match anything,\n      When using bump deploy, an explicit message is displayed if the target directory doesn’t exist.\n    \n  \n  Security badges are now displayed for webhooks,\n  CLI and GitHub Action code samples in your hub/documentation settings now contain real slugs and tokens to speed up Bump.sh integration into your workflow.\n\n\nFixes:\n\n  Autogenerated date-time and time string examples now follow the ISO RFC 3339 format recommended by OpenAPI, and are displayed as 2017-07-21T17:32:28Z,\n  The markdown support in documentation has been audited and multiple visual enhancements and fixes have been applied, improving visual hierarchy and readability,\n  Security Schemes from OpenAPI V2 definition files are now displayed in the documentation,\n  The top navigation has been reworked to offer a consistent experience across all views (Documentation, Changelog, API Explorer),\n  Security badges have been reworked to ensure that they always correctly redirect to the authentication page and are displayed consistently,\n  Descriptions of array[object] are now displayed in the documentation.\n\n\nAs usual, don’t hesitate to reach out if you have any questions, feedback, or features you would like to see added to the product. Knock at hello@bump.sh"
        },
        {
          "id": "product-updates-2025-05-20-api-explorer-better-response-experience",
          "title": "API Explorer - Better response experience",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2025/05/20/api-explorer-better-response-experience/",
          "content": "Following feedback, we have improved the experience of the API Explorer response module.\n\nIncreased readability\nThe response module can now be expanded, making it easier to read long responses.\n\n\n\nImages support\nIf an API returns an image, the API Explorer will now display it directly in the response module (for content-type: image/jpeg and image/png).\n\n\n\nWant to try the API Explorer? You can request access here.\n\nThe API Explorer is still in beta. We warmly welcome your feedback."
        },
        {
          "id": "product-updates-2025-07-15-documentation-fixes-and-improvements",
          "title": "Documentation fixes and improvements",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2025/07/15/documentation-fixes-and-improvements/",
          "content": "Even if our product grows with new features, taking the time to maintain, enhance, and polish existing features is crucial to ensure a solid documentation experience. That’s why we always give ourselves time to tackle small enhancements and fixes alongside the development of new features. Here’s a list of what’s been worked on.\n\nImprovements:\n\n  You can now add more than one additional link to the navigation bar by using our new custom extension called x-externalLinks instead of the externalDocs property,\n  When the link to an operation or group no longer exists, it now returns a 404 page instead of redirecting to the top of the documentation, providing a clearer feedback,\n  When switching from one branch to another, the active operation is maintained if it also exists in the target branch,\n  The global stability of the platform has been improved by optimizing requests and setting up deeper cache strategies. It’s an ongoing endeavour that we’ll continue for the next few months,\n  We’ve updated our status page and how we communicate through it, to share precise information about the production status in real-time. It can be followed on status.bump.sh,\n  Meta descriptions are now context-specific (topic, endpoint, channel, operation, webhook…),\n  Enums support in query parameters has been deepened to handle complex combinations,\n  The sharing module appearing on hover no longer prevents from easily copying operation and property names,\n  Badges generated using the x-state property no longer have a string length limit,\n  Mermaid diagrams and charts added in Markdown now support custom text colors,\n  The switch between oneOfs now displays the discriminator mapping name instead of the component name: the mapping name value generally gives more information about the real use of an API consumer.\n\n\nFixes:\n\n  Revamped the navigation bar architecture to ensure its stability in every context (with or without logo, inside a hub or as a standalone documentation, in the documentation, API Explorer, changelog, …),\n  Reworked the copy button of the code sample module to keep it visible when the path is longer than the module, and make sure it always copies the active content when multiple code samples are defined,\n  Reworked oneOfs support to ensure the right titles and content are always displayed and clickable,\n  Fixed an issue where selecting a different content-type returned an empty body,\n  Fixed missing required badges in advanced oneOf contexts,\n  Fixed a glitch hiding the title of the hub in dark mode,\n  Added a line break to externalDocs strings to ensure they wrap with the documentation width,\n  Fixed multiple API Explorer UI consistency issues when applying custom styles,\n  Fixed missing headers in AsyncAPI in specific contexts.\n\n\nAs always, don’t hesitate to reach out if you have any feedback, questions, or features you think are missing from the product. Knock at hello@bump.sh"
        },
        {
          "id": "product-updates-2025-07-25-embed-mode",
          "title": "Introducing Embed mode",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2025/07/25/embed-mode/",
          "content": "Thanks to Embed mode, documentation can now be fully integrated in your global navigation, to provide your users with one unified experience. \nBy integrating your Bump.sh documentation inside your own frontend stack, you get complete control over your branding and navigation.\n\nTo activate Embed mode, three steps are needed:\n\n  Set up a reverse proxy in front of Bump.sh that can rewrite and forward traffic correctly,\n  Add the required headers X-BUMP-SH-PROXY and X-BUMP-SH-EMBED,\n  Inject your topbar, footer in the dedicated HTML tags.\n\n\nDiscover our integration guide in our help center\n\n\nEmbed mode used by MongoDB\n\nDon’t hesitate to reach out if you have any feedback regarding this freshly released feature."
        },
        {
          "id": "product-updates-2025-08-14-api-explorer-server-variables",
          "title": "API Explorer - Server variables",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2025/08/14/api-explorer-server-variables/",
          "content": "Server variables enable API Explorer users  to customize the request server with predefined enums or custom strings. It’s useful to specify a server region or a client specific instance, for example.\n\nVariables are defined in a server object:\nservers:\n  - url: https://{region}.bump.sh/{docId}\n    description: The production API server\n    variables:\n      docId:\n        description: Can be found in your documentation settings.\n      region:\n        enum:\n          - \"east-eu\"\n          - \"west-eu\"\n        default: \"east-eu\"\n\n\nIn this example, two variables were added:\n\n  docId is a string, and a description has been defined to help the API user find its value,\n  region is an enumerated list, with east-eu being the default value.\n\n\nThe API Explorer renders each variable accordingly.\n\n\nLearn more about it in our help center\n\nAny feedback on this feature? We’re happy to hear them! Say hi at hello@bump.sh."
        },
        {
          "id": "product-updates-2025-09-08-api-explorer-oauth2-implicit-flow",
          "title": "API Explorer - OAuth2 implicit flow support",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2025/09/08/api-explorer-oauth2-implicit-flow/",
          "content": "Supporting the “implicit” OAuth2 security scheme allows for an automated login experience for your API consumers in the API Explorer.\nTo activate it, a vendor extension called x-client-id can be added to the definition file. Its value should be the ID of a dedicated OAuth client application created on your Authorization server to identify your API consumers.\n\nThe x-client-id property is added to your OAuth2 implicit flow object:\ncomponents:\n  securitySchemes:\n    \"OAuth2 implicit flow\":\n      type: oauth2\n      flows:\n        implicit:\n          authorizationUrl: \"https://auth.example.org/oauth\"\n          scopes: {}\n          x-client-id: \"123456abcdef\"\n\n\nExample of the OAuth2 implicit flow experience for an API consumer already logged into the API’s cloud platform.\n\nLearn more about it in our help center\n\nAs always, don’t hesitate to reach out at hello@bump.sh if you have any feedback!"
        },
        {
          "id": "product-updates-2025-09-17-custom-meta-tags",
          "title": "Custom meta tags",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2025/09/17/custom-meta-tags/",
          "content": "Custom meta tags allow you to add your own meta tags to the head of your documentation, for example, to enhance your own global search experience with filtering through facets based on these meta tags.\n\nThe x-metaTags array is composed of objects made of two elements:\n\n  name: the value that will be given to the name attribute,\n  content: the value that will be given to the content attribute.\n\n\nAdding the x-metaTags to the root of your definition:\nx-metaTags:\n  - name: First meta tag\n    content: First tag content\n  - name: Second meta tag\n    content: Second meta tag content\n\n\nWill apply the tags in the HTML of your documentation page:\n&lt;head&gt;\n  &lt;meta name=\"First meta tag\" content=\"First meta tag content\" /&gt;\n  &lt;meta name=\"Second meta tag\" content=\"Second meta tag content\" /&gt;\n&lt;/head&gt;\n\n\nYou can apply it at different levels of your API definition: root, topic, operation, … See the full list in our help center\n\nAny feedback or requests? Don’t hesitate to reach out at hello@bump.sh!"
        },
        {
          "id": "product-updates-2025-09-29-documentation-enhancements-and-fixes",
          "title": "Documentation enhancements and fixes",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2025/09/29/documentation-enhancements-and-fixes/",
          "content": "While achieving milestones on larger projects, we’ve also made many quality-of-life enhancements and bug fixes. Here’s the changelog:\n\nEnhancements:\n\n  Custom meta tags can now be added to the head of your documentation,\n  Dead anchors now redirect to their parent operation instead of the top of the documentation,\n  In Embed mode, the search placeholders now specify their context (« Search API documentation » vs « Search hub »), so users can differentiate the global search from the documentaion portal search,\n  A confirmation modal has been added to the API Explorer before sending destructive requests to avoid unwanted losses,\n  The scrolling experience has been globally reworked to ensure its stability,\n  Hub categories can now filter the documentation selector in the top navigation bar. This way, readers only see relevant docs from the same category, improving navigation for them and giving admins clearer control over doc organization,\n  The browser window.title is now automatically updated when scrolling between operations to display the current navigation context,\n  Highlights now work for subtitles inside x-topics, and global/operation descriptions,\n  x-feedbackLink now works with Swagger 2.0 definition files,\n  Groups in the navigation bar are now collapsible, to facilitate the navigation between groups containing many operations,\n  The “add custom domain” module has been reworked to provide better explanations and error messages,\n  Branches can now be sorted alphabetically in ascending and descending order,\n  Skeletons for previews have been enhanced,\n  x-state badges can now have custom colors,\n  Accordions can now be added in Markdown using &lt;details&gt; and &lt;summary&gt; tags,\n  A logo dedicated to dark mode can now be added to ensure brand consistency,\n  The API Explorer beta is completed. 🎉 Thanks a lot to all of our testers. Your feedback has been invaluable and helped us build an even better feature for all.\n\n\nFixes:\n\n  Fixed wrong breaking changes when the case of an HTTP header was modified but not its value,\n  Fixed a trailing issue that caused the branch switch to return 404s in some contexts,\n  Fixed multiple text alignments in documentation,\n  Fixed an issue that prevented some descriptions from being displayed in the documentation when defined next to allOfs,\n  Fixed an issue with alternatives preventing some generated examples from being displayed in some contexts,\n  Fixed a bug with Mermaid rendering that prevented the use of ELK diagrams,\n  Fixed an issue that displayed the “Show more” button in properties even when everything was already displayed in some contexts.\n\n\nFeel free to reach out if you have any questions or features you think are missing from the product. Knock at hello@bump.sh."
        },
        {
          "id": "product-updates-2025-10-16-dark-mode-customization",
          "title": "Dark mode customization",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2025/10/16/dark-mode-customization/",
          "content": "Since we started providing dark mode, we applied filters to make your light mode color and logo blend in dark mode. The thing is, it isn’t always enough to ensure branding consistency. You can now define a custom logo and color dedicated to dark mode.\n\nThe option is available in your hub/documentation settings, under “Customize UI”.\n\n\n\nIt’s not mandatory: we’ll keep applying filters if you don’t want/need to customize it, but it gives you flexibility.\n\nAny request or feedback? As usual, you can reach out at hello@bump.sh!"
        },
        {
          "id": "product-updates-2025-11-19-new-search-experience-10-times-faster",
          "title": "New search experience",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2025/11/19/new-search-experience-10-times-faster/",
          "content": "Search is at the core of our doc portal experience: your users need to easily find what they’re looking for, as it’s a key part of their API discovery.\n\nBecause we know how crucial it is, we recently completely rebuilt it to make it more relevant, faster, and more reliable.\n\nRelevance\nHaving quick results is useless if they aren’t relevant. We have redesigned our relevance rules from scratch to ensure that we provide your users with what they are really looking for:\n\n  We now display a single list of results instead of several categories: users mostly look for operations, so displaying properties and other content “by default” didn’t always make sense.\n  That global result list is ordered based on how closely the query matches a result item, and on its type. For example, for the same result weight, an operation will be favored over a property,\n  The depth of a result is also taken into consideration to calculate its relevance, because we know that a first-level property is generally more important (and therefore searched) than a deep property,\n  We removed the 5 results per type limit and now return all relevant results,\n  Typos are also now better managed, ensuring relevant results even when the search query contains typos.\n\n\nPerformance\nQuickly finding the right information is one of the keys to a good API discovery, and search is part of it. Search speed has been increased by a factor of 10. This is particularly visible on larger hubs and complex documentation, reducing the time from almost a second to a few milliseconds, making the search experience instantaneous.\n\n\n\nUsability\nAll this work is under the hood, but we also made improvements to the search interface itself: the visual hierarchy of result items has been fully reworked to increase readability.\n\n\n\nAny feedback? Reach out at hello@bump.sh!"
        },
        {
          "id": "product-updates-2025-12-08-ask-ai-markdown-rendering",
          "title": "Ask AI, Markdown rendering & llms.txt",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2025/12/08/ask-ai-markdown-rendering/",
          "content": "Many API consumers now rely on AI tools to help them quickly discover API capabilities. Conversational tools such as ChatGPT or Claude can be fed with documentation intended for humans, but that extra “human context” gives them more noise, impacting the AI tools’ relevance and increasing token costs.\n\nOur latest release provides these tools with the right context, empowering your API doc consumers through their favorite AI tools.\n\nAsk AI\nUsers can now easily ask questions about an API in their go-to conversational tools and get top-notch results. An “Ask AI” option has been added in documentation next to its introduction, x-topics, groups, and operations, offering access to ChatGPT and Claude with the right, narrowed-down context, alongside a one-click Markdown access.\n\n\n\nMarkdown rendering\nBump.sh now offers an alternative context optimized for AI tools through Markdown rendering.\n\nTo access the Markdown version of a documentation, simply add .md at the end of the URL (or /source.md if you have a custom domain, when the URL is the root of your documentation).\n\nOn a documentation root (example truncated for visibility purposes).\n# Bump.sh Api\n\n## Description\nThis is version `1.0` of this API documentation. Last update on Nov 28, 2025.\nThis is the official Bump.sh API documentation. Obviously created with Bump.sh.\n\nThe Bump.sh API is a REST API. It enables you to [...]\n\n\n## Servers\n- Production: https://bump.sh/api/v1 ()\n\n\n## Topics\n- [Authentication](https://developers.bump.sh/authentication.md)\n\n\n## Endpoints and operations\n\n### [Branches](https://developers.bump.sh/group/endpoint-branches.md)\n- [Promote branch as the default one](https://developers.bump.sh/operation/operation-patch-docs-parameter-branches-parameter-set_default.md)\n- [Delete a branch](https://developers.bump.sh/operation/operation-delete-docs-parameter-branches-parameter.md)\n- [List available branches](https://developers.bump.sh/operation/operation-get-docs-parameter-branches.md)\n- [Create a new branch](https://developers.bump.sh/operation/operation-post-docs-parameter-branches.md)\n### [Diffs](https://developers.bump.sh/group/endpoint-diffs.md)\n- [...]\n\n\n## Webhooks\n\n### [Documentation change](https://developers.bump.sh/group/webhook-documentation-change.md)\n- [Structure change](https://developers.bump.sh/operation/operation-webhookdocstructurechange.md)\n\n\nFor a specific operation (example truncated for visibility purposes).\n# Create a new version\n\n**POST /versions**\n\nDeploy a new version for a given documentation, which will become the current version.\n\n\n## Servers\n- Production: https://bump.sh/api/v1 ()\n\n\n## Authentication methods\n- Authorization token\n- Basic token (deprecated)\n\n\n## Parameters\n\n### Body: application/json (object)\nThe version creation request object\n- **documentation** (string)\n  UUID or slug of the documentation.\n- [...]\n\n\n## Responses\n### 201\nDocumentation version successfully created\n\n#### Body: application/json (object)\n- **id** (string)\n  Unique id of your version.\n- [...]\n\n\nllms.txt\nLLMs crawlers face similar challenges when crawling API docs: they don’t know what they’re looking for, and are flooded with an overabundance of information. llms.txt provides context, telling crawlers what information can be retrieved behind each page of a documentation.\n\nllms.txt is available on both hubs and docs, by adding /llms.txt at the end of the URL.\n\nOn a documentation (example truncated for visibility purposes).\n# Train Travel API\n\n## Description\nAPI for finding and booking train trips across Europe.\n\n## Servers\n- Production: https://api.example.com (Production)\n\n## Topics\n- [Getting started](https://bump.sh/bump-examples/doc/train-travel-api/topic/topic-getting-started.md)\n\n## Endpoints and operations\n\n### [Stations](https://bump.sh/bump-examples/doc/train-travel-api/group/endpoint-stations.md)\n- [Get a list of train stations](https://bump.sh/bump-examples/doc/train-travel-api/operation/operation-get-stations.md)\n- [...]\n\n\nDon’t hesitate to reach out at hello@bump.sh if you have any questions/feedback."
        },
        {
          "id": "product-updates-2025-12-22-openapi-3-2-partial-support",
          "title": "OpenAPI 3.2: partial support",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2025/12/22/openapi-3-2-partial-support/",
          "content": "The OpenAPI Initiative released a new version of their OpenAPI specification in September: OpenAPI 3.2.\n\nIf you didn’t hear about this new version, feel free to read our OpenAPI complete guide, updated for OpenAPI 3.2.\n\nWe started supporting some of the features brought by this new version:\n\n  The QUERY method is now supported in both the documentation and the API Explorer.\n  Servers can now have a name, which can be combined with the existing description field to provide users with a clear context about a server’s usage.\n  Tags now offers a summary property. Behaving like our existing x-displayName vendor extension, it gives a way to display a group name in the documentation that is different from the tag name.\n  summary can be added to responses when the level of detail doesn’t justify the use of the existing description field.\n  Security schemes can now be deprecated.\n\n\nOpenAPI 3.2 definition files can be deployed through your usual deployment process: using our Github Action, our CLI, our API (now in OpenAPI 3.2 ✨), or directly in your Bump.sh dashboard.\n\nAs always, you can reach out at hello@bump.sh for any questions/feedback."
        },
        {
          "id": "product-updates-2026-03-26-introducing-bump-mcp-platform",
          "title": "Introducing Bump.sh MCP platform",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2026/03/26/introducing-bump-mcp-platform/",
          "content": "APIs are no longer consumed only by humans. AI agents need to interact with them too, in a reliable and controlled way.\n\nMost approaches today rely on naive OpenAPI-to-MCP transformations: individual endpoints exposed as tools, no orchestration, no security layer. The result is fragile, hard to maintain, and risky to run in production.\n\nThis is why we have built the Bump.sh MCP Platform: thanks to declarative workflows, you can now turn your APIs into deterministic, production-ready MCP servers. No guessing at runtime, no glue code, no credentials leaking to the LLM.\n\nNot a simple OpenAPI-to-MCP conversion\n\nBump.sh MCP servers expose ready-to-use workflows, not individual endpoints. You define the goals your API users can achieve (e.g. “book a train trip”), and the MCP server follows the workflow you defined, step by step.\n\nThis means you keep full control of the orchestration. AI tools consume fewer tokens, and the execution is predictable and repeatable.\n\nDeclarative by design\n\nYour MCP servers are based on workflow documents written in YAML. No hidden logic: everything is versionable, reviewable, and CI/CD-friendly. If you already use Bump.sh for API documentation, the experience is the same: update your definition, deploy from your CI, iterate with your team.\n\nTwo specifications are supported:\n\n  Arazzo (the OpenAPI Initiative standard) if you already have OpenAPI definitions\n  Flower, our lightweight specification for simple cases where no OpenAPI document is needed\n\n\nBuilt-in security\n\nCredentials never go through the LLM. Store your API keys using Secrets: they stay server-side, only used to authenticate requests to your APIs.\n\nMCP servers can be private, just like API docs. Manage who gets access directly in Bump.sh or using your own custom authentication server. Thanks to our integrated OAuth support, your users authenticate in one click, directly in their AI tools.\n\nWhen an AI agent calls your MCP server, the data plane is the component that executes your workflows and forwards requests to your APIs. It runs on a server separated from Bump.sh’s main infrastructure, and no sensitive data is stored or logged. By default (on self-service plans), the data plane is hosted by Bump.sh. For custom plans, you can host it in your own infrastructure to get full control over your data.\n\nObservability and debugging\n\nWhen things go wrong with API workflows, identifying the root cause can get tricky. Each MCP server comes with a built-in live debugger that lets you follow each step of a workflow execution in real time. Debug sessions are secured and fully historized: you know exactly who triggered a session and when, giving you a complete audit trail of your debugging activity.\n\nJoin the closed beta\n\nBump.sh MCP Platform is currently in closed beta. Sign up here to get early access and help us shape the product.\n\nIf you have any questions or feedback, reach out to us anytime."
        },
        {
          "id": "product-updates-2026-03-31-search-in-all-apis",
          "title": "Search in all APIs",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "Improvement",
          "url": "/product-updates/2026/03/31/search-in-all-apis/",
          "content": "Until now, running a search inside an API doc could only search the active doc. With search scopes, you can now search an entire hub without leaving the API doc. This one-click switch from a doc-only search to a global hub-search ensures your users no longer get stuck with an empty search results list and no follow-up actions.\n\n\n\nYou can change the default search scope (current API or global) in your hub settings. API users can individually activate/deactivate it in the API search modal.\n\nAny requests or feedback? Reach out at hello@bump.sh!"
        },
        {
          "id": "product-updates-2026-04-07-roles-requirements",
          "title": "Roles requirements",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2026/04/07/roles-requirements/",
          "content": "Your users need to know what authentication methods are required, but they also have to know what access level or roles are needed to start sending requests to specific endpoints. There’s currently no way to describe it in OpenAPI: that’s why we are introducing x-rolesRequirements. Combined with security schemes, it gives your users a complete picture.\n\nThe x-rolesRequirements property can be added to any OpenAPI/AsyncAPI operation. It can either be an array of strings or a string:\n\npaths:\n  /clusters:\n    post:\n      summary: Create a new cluster\n      x-rolesRequirements:\n        - Organization owner\n        - Product owner\n      # ... rest of operation\n  /clusters/{id}:\n    get: \n      summary: Get cluster details\n      x-rolesRequirements: \"Cluster privileges: read\"\n      # ... rest of operation\n\n\n\n\nChanges to role requirements don’t create changelog entries: you can update them without affecting version history.\n\nLearn more about it in our help center\n\nAs always, don’t hesitate to reach out if you have any feedback."
        },
        {
          "id": "product-updates-2026-04-17-mcp-server-arazzo-support",
          "title": "Run Arazzo workflows on your MCP servers",
          "collection": {
            "label": "product-updates",
            "name": "Product Updates"
          },
          "categories": "",
          "tags": "New",
          "url": "/product-updates/2026/04/17/mcp-server-arazzo-support/",
          "content": "You can now use your Arazzo documents to run workflows on Bump.sh MCP servers. Arazzo is a workflow specification from the OpenAPI Initiative that extends your existing OpenAPI documents by describing how to sequence operations to accomplish real tasks.\n\nYou can deploy Arazzo documents using the CLI or the GitHub Action. Web upload using the dashboard isn’t supported yet.\n\nWanna learn more about Arazzo? Head over to our Arazzo guide for a full walkthrough of the specification.\n\nChoosing between Arazzo and Flower\n\nYou can now describe your workflows using one of these two specifications:\n\n  Flower, our own lightweight specification.\n  Arazzo, the specification supported by the OpenAPI Initiative.\n\n\nWe initially built Flower to describe simple workflows without having to add references to OpenAPI files: the specification felt overkill during our MCP platform building phase. As we started writing more complex workflows, we deepened Flower capabilities. It now supports data transformation between steps that Arazzo doesn’t support.\n\nYou can use Arazzo when you have OpenAPI definitions and want the portability of an OAI standard, and use Flower when you need something lighter or deeper data manipulation at the step level.\n\nAny questions or feedback? Reach out to us anytime."
        },
        {
          "id": "guides-technical-writing-incorporating-api-documentation-guidelines-into-your-api-style-guide",
          "title": "Incorporating API Documentation Guidelines Into Your API Style Guide",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Technical Writing",
          "tags": "",
          "url": "/guides/technical-writing/incorporating-api-documentation-guidelines-into-your-api-style-guide/",
          "content": "For many organizations, an API style guide is a well-established document. It outlines the conventions and patterns that developers should follow when building APIs. These guides typically cover topics like naming conventions for resources and parameters, request and response structures, and error handling. However, a crucial element often is missing: API documentation guidelines.\n\nThis article explores the importance of incorporating API documentation best practices into your API style guide. We’ll explore the benefits, what to include in your guidelines, and conclude with an example you can use as a starting template for your own standards and practices.\n\nWhy Documentation Guidelines Matter\n\nEvery developer writes API documentation in their own unique style. Some might be concise, others overly verbose. Some might lack crucial information, while others drown users with unnecessary details. In some cases, the documentation focuses more on the internal implementation details than how to consume the API. This inconsistency creates a frustrating experience for API consumers, hindering adoption and increasing support costs.\nHere’s how clear and consistent documentation guidelines benefit everyone:\n\nImproved Developer Experience: Users can quickly grasp the API’s capabilities and how to interact with it. Consistent formatting and structure make it easier to navigate the documentation.\n\nReduced Support Costs: Well-documented APIs require less explanation and troubleshooting, freeing up developer resources.\n\nFaster Onboarding: New team members and external developers can learn the API quickly thanks to clear and consistent explanations alongside examples to jumpstart the process.\n\nBranding and Professionalism: Polished documentation reflects positively on your organization, your commitment to your API, and your internal development practices.\n\nEnhanced Internal Collaboration: Consistent documentation fosters a shared understanding within the development team and helps the onboarding of new team members that will be working on the API team.\n\nWhat to Include in Your Documentation Guidelines\n\nNow that we understand the importance of documentation guidelines, let’s explore what elements to incorporate into your style guide:\n\nContent Standards:\n\nClarity and Conciseness: Emphasize the importance of clear, unambiguous language, avoiding technical jargon, abbreviations, and internal team names or system names.\n\nTarget Audience: Consider the variety of roles for your intended audience (developers, operations, decision makers, etc.) and tailor the content to address each level of detail accordingly.\n\nConsistency: Define standards for terminology, tone, and voice, ensuring a consistent style throughout the documentation.\n\nStructure and Organization:\n\nHierarchy: Outline a logical hierarchy for organizing information, starting with setting the context/purpose of the API, what it does/doesn’t do, then the more technical elements such as resources and operation details.\n\nFormatting: Specify formatting conventions for headers, code snippets, tables, and links. This will provide polish and a sense of professionalism rather than an ad-hoc style that looks like it was thrown together a few minutes before it was released.\n\nNavigation: Ensure clear and consistent navigation within the documentation, allowing users to easily find what they need. This may not be a concern on day 1, but over time your documentation will grow and your navigation will need to be reevaluated through a lens of information architecture and user journeys.\n\nContent Requirements:\n\nMinimal Documentation: Define the minimum set of information required for each API endpoint (e.g., description, request parameters, response structure, example property values).\n\nOptional Content: Outline additional information that can be included for more complex APIs (e.g., examples, authentication details, best practices). In some cases, this content can be shared across multiple APIs, but they should be referenced and linked within the documentation.\n\nVersioning and Change Management:\n\nChangelogs: Specify the format and location for change logs, documenting updates and API revisions.\n\nVersioning of Documentation: Outline how documentation is versioned alongside the API itself, ensuring users access the correct documentation for their API version. This may include using the most recent version as the default documentation set, along with links to previous versions for those that haven’t migrated to the latest version yet.\n\nStyle and Tone:\n\nVoice and Tone: Define the voice and tone of the documentation (e.g., formal, informal, helpful).\n\nCode Examples: Specify the formatting and commenting conventions for code samples.\n\nCode Snippets and Examples:\n\nProgramming Language: Define the programming language(s) used in code snippets for consistency.\n\nError Handling: Include examples of how to handle different error responses, such as reaching a rate limit and when to refresh an API token.\n\nTooling and Resources:\n\nRecommended Tools: Suggest tools that can be used to generate and manage API documentation (e.g., Swagger, OpenAPI). This may include adding the API to an internal API catalog and/or external API marketplace (for instance, by using APIs.json).\n\nLinks and References: Provide a list of relevant resources, such as the official API style guide and external documentation standards, if available.\n\nImplementing Your Documentation Guidelines\nHere are some tips for effectively implementing your documentation guidelines:\n\nAccessibility: Ensure documentation is accessible to users with disabilities, following WCAG standards.\n\nCommunity Involvement: Encourage feedback from developers and API consumers to continuously improve the documentation.\n\nTraining and Resources: Provide training sessions and resources to familiarize developers with the style guide.\n\nStyle Guide Enforcement: Utilize tools or checklists to ensure documentation adheres to the guidelines during the development process.\n\nA Sample API Documentation Style Guide Section\nLet’s see how these elements can be incorporated into a formal style guide. The content is offered in Markdown for easy copy-and-paste into your own style guide:\n\n## 3. API Documentation\nThis section outlines the guidelines for documenting APIs within the organization.\n\n### 3.1 Content Standards\nClarity and Conciseness: Documentation should be written in clear, concise, and unambiguous language. Avoid technical jargon whenever possible. Explain complex concepts in a way that is easy for the target audience to understand.\nTarget Audience: Identify the intended audience for the documentation (e.g., developers, operations team, system administrators). Tailor the level of detail and technical depth accordingly.\nConsistency: Maintain a consistent style throughout the documentation in terms of terminology, tone, and voice. Define a glossary of terms to ensure consistent usage.\n\n### 3.2 Structure and Organization\n\nHierarchy: Organize information logically, grouping related resources by functionality. Use a clear hierarchy with headings and subheadings to guide users.\nFormatting: Specify consistent formatting conventions for elements like:\nHeaders (H1, H2, etc.)\nCode snippets (including syntax highlighting and indentation)\nTables (including column headers and alignment)\nLists (bulleted and numbered)\nLinks (internal and external, including proper anchor text)\nNavigation: Ensure easy navigation within the documentation. Utilize a table of contents, breadcrumbs, and clear links to allow users to find specific information quickly.\n\n### 3.3 Content Requirements\n\nMinimal Documentation: Define the minimum set of information required for each API endpoint. This typically includes:\nA clear and concise description of the endpoint's purpose.\nA list of request parameters, including their data type, format, and whether they are required or optional.\nThe expected response structure, including data types and descriptions of response fields.\nError codes and their corresponding meanings.\nOptional Content: For complex APIs, consider including additional information such as:\nAuthentication and authorization details.\nCode samples demonstrating how to interact with the API using different programming languages.\nBest practices and usage guidelines.\nExamples of common use cases.\n\n### 3.4 Versioning and Change Management\n\nChangelogs: Maintain a changelog that documents updates and revisions made to the API. Specify the format and location of the changelog within the documentation.\nVersioning of Documentation: Version the documentation alongside the API itself. Indicate the corresponding API version for each documentation set. This ensures users access the correct documentation for their specific API version.\n\n### 3.5 Style and Tone\n\nVoice and Tone: Define the voice and tone of the documentation. Consider factors like the target audience and overall brand image. Options might include:\nFormal and professional\nInformative and helpful\nConversational and approachable\nCode Examples: Specify the formatting and commenting conventions for code snippets used throughout the documentation. Ensure code samples are well-commented and easy to understand.\n\n### 3.6 Tooling and Resources\n\nRecommended Tools: Suggest tools that can be used to streamline generating and managing API documentation. Examples include Swagger, OpenAPI Specification (OAS), or API Blueprint.\nLinks and References: Provide a list of relevant resources for developers, such as:\nThe official API style guide document.\nExternal documentation standards (e.g., RESTful API Design).\nLinks to online communities or forums for API development.\n\n### 3.7 Additional Considerations\n\nAccessibility: Ensure the documentation is accessible to users with disabilities by adhering to WCAG (Web Content Accessibility Guidelines) standards.\nCommunity Involvement: Encourage feedback from developers and API consumers to continuously improve the documentation. This can be done through surveys, forums, or dedicated feedback channels.\nTraining and Resources: Provide training sessions and resources to familiarize developers with the API style guide and documentation best practices.\nStyle Guide Enforcement: Consider utilizing tools or checklists to ensure documentation adheres to the guidelines during the development process. This can help maintain consistency and quality.\n\n\nThis example will help you get started. Of course, you can add RFC 2119 language to make these more formalized and enforceable. Remember, the specific content and details will vary depending on your organization’s needs and the complexity of your APIs, so be sure to spend some time reviewing and adjusting these recommendations.\n\nConclusion\nIntegrating comprehensive documentation guidelines into your API style guide enhances not only the usability of your APIs but also their acceptance and longevity in the market. A detailed API documentation guideline helps ensure consistency, fosters trust, and enhances user experience, all of which are crucial for the successful deployment and adoption of APIs. By following these recommendations and tailoring them to your specific context, you can cultivate clear, consistent, and user-friendly API documentation that empowers developers and fosters a thriving API ecosystem."
        },
        {
          "id": "guides-bump-sh-tutorials-mock-server-with-beeceptor",
          "title": "Mocking APIs with Beeceptor",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Bump-sh tutorials",
          "tags": "",
          "url": "/guides/bump-sh-tutorials/mock-server-with-beeceptor/",
          "content": "When building distributed software systems, from an e-commerce storefront to a logistics API, each component is deeply tied to others. In this setup, waiting for dependencies can slow down engineering productivity. If the backend isn’t ready, the frontend team stalls. If partner APIs are flaky or have no sandbox, QA engineers can’t write tests.\n\nMocking APIs is an essential part of modern developer development. For those following an API design-first workflow, the next step is to enable all stakeholders to start working with the API early.\n\nBeeceptor is a tool that can help. It’s a no-code mock server that transforms your OpenAPI spec into a live API that responds with realistic payloads. Let’s walk through the process of using Beeceptor for this purpose.\n\nWhat is Beeceptor?\n\nBeeceptor is a no-code tool for mocking APIs. It provides a lightweight approach to service virtualization. With Beeceptor:\n\n\n  Teams can simulate downstream or upstream services without writing any backend code.\n  Frontend, QA, and partner integrations can begin early using realistic mock APIs.\n  You don’t need to manage infrastructure, container platforms, or CI/CD pipelines to prototype an API.\n\n\nFrom OpenAPI to Realistic Responses\n\nYou can take either a YAML or JSON OpenAPI file and start a mock server. If your spec is hosted publicly (on Bump.sh or Github), you can provide a public URL directly, and Beeceptor will fetch and parse it. Once imported, Beeceptor intercepts the following to build a dedicated mock server page:\n\n  All the defined paths, methods, and operationIds\n  Request and response payload schemas\n  Parameters, headers, and query definitions\n\n\nIt then plugs a test data generator to create dynamic responses. Field names, descriptions, JSON-Schema, and formats (e.g. enums, date-time, emails, prices) are all considered to produce realistic payloads.\n\n\n  Have your OpenAPI spec published via Bump.sh? You can import it into Beeceptor and use the generated mock server URLs directly inside your spec’s servers block for interactive try-outs.\n\n\nCreating the mock server is quick.\n\n\n\n\n\nYou can open the mock server’s dashboard to review the specification’s path, request, and response payload.\n\n\n\nHere is a sample API response when invoking the Get All Products API. Instead of placeholder values, you get domain-specific values without any extra configuration. Checkout the following response payload and refer to the name, price, stock, and created_at for the generated values.\n\nGET /products\n\nHTTP/1.1 200 OK\nContent-Type: application/json\n\n[\n  {\n    \"id\": \"052c50e6-0b41-47e0-8376-314845db5b43\",\n    \"name\": \"Refined Aluminum Chair\",\n    \"price\": 68.83,\n    \"stock\": 37,\n    \"category\": \"Electronics\",\n    \"image_url\": \"https://picsum.photos/seed/bEc1um1r/766/809?blur=8\",\n    \"created_at\": \"2023-12-18T00:00:00.0Z\",\n    \"description\": \"Discover the elephant-like agility of our Bacon, perfect for rich users\",\n    \"updated_at\": \"2025-06-05T00:00:00.0Z\"\n  },\n  {\n    \"id\": \"bb637eb4-76e2-4d83-be33-442b8ff4bc2e\",\n    \"name\": \"Tasty Bamboo Pants\",\n    \"price\": 224.73,\n    \"stock\": 67,\n    \"category\": \"Outdoors\",\n    \"created_at\": \"2024-02-27T00:00:00.0Z\",\n    \"image_url\": \"https://picsum.photos/seed/b0PetW5/766/809?grayscale&amp;blur=7\",\n    \"description\": \"Fantastic Keyboard designed with Rubber for quarterly performance\",\n    \"updated_at\": \"2025-06-17T00:00:00.0Z\"\n  }\n]\n\n\nCustomizing Mocks with Rules\n\nWhile Beeceptor generates mock responses from your OpenAPI spec, real-world testing often demands more control, like responses that change based on headers, query parameters, request bodies, or session context. This is where Mock Rules come in.\n\nAt Beeceptor, the mock rules are evaluated top-down, and the first matching rule is applied. When a rule is matched, Beeceptor sends the defined response and bypasses the default OpenAPI mock logic. This gives you complete control over the mocking behavior.\n\nKey Capabilities\n\n\n  Priority Handling: Rules always take precedence over the auto-generated OpenAPI responses.\n  Flexible Matching: Match on HTTP method, path, query params, headers, and body content (exact or regex).\n  Network Behavior Simulation: Add artificial delays (e.g., delay: 2000ms) or drop connections to test edge cases.\n  Error Simulation: Easily return 500 / 401 / 429 responses under specific test conditions.\n  Dynamic Personalization: Define responses per user or role. E.g.: When hitting GET /products, the response changes based on who logged in.\n    Header: X-User-ID: userA → returns products [A,B]\nHeader: X-User-ID: userB → returns products [B,C]\n    \n  \n\n\nBuild Stateful Mocks\n\nModern mocking is about more than static JSON; it’s about simulating behaviors. To simulate dynamic, multi-step application behavior, stateful mocking is essential. Beeceptor supports this with a persistent state engine.\n\nWith stateful mocks, your mock endpoints can:\n\n\n  Remember data across requests\n  Simulate object creation and retrieval\n  Track counters (e.g., order IDs, session steps)\n  Emulate queue-like flows (e.g., shopping cart, job queues)\n\n\nBeeceptor achieves this through three fundamental operators:\n\n\n  Step Counters – for sequential values or stage tracking. E.g. counting page views, or total orders.\n  Data Stores – for key-value persistence. E.g. storing current logged in user name.\n  Lists – for storing a collection of items. E.g. shopping cart, to-do lists, etc.\n\n\nYou should user these in the response templates using a simple Handlebars syntax. This syntax replaces scripting.\n\nExample: Add to Cart and View Cart\n\nLet’s say you want to simulate a shopping cart workflow using two endpoints:\n\nPOST /cart/items → Add item to the cart\n\n\nGET /cart → List current cart contents\n\n\nAdd Item to Cart (POST /cart/items)\n\nHere’s a response template that adds the incoming item to a persistent list named shoppingCart:\n\n{{ list 'push' 'shoppingCart' (body) }}\n{\n  \"message\": \"Item added to cart\",\n  \"item\": {{ body }}\n}\n\n\nHere,\n\n  (body) captures the full incoming JSON payload\n  list 'push' appends it to the mock-side list\n\n\nThis list persists across calls for the endpoint.\n\nView Cart (GET /cart)\n\nNow, when you query the cart, you can retrieve the entire list:\n\n{\n  \"cartItems\": {{ list 'get' 'shoppingCart' }},\n  \"totalItems\": {{ len (list 'get' 'shoppingCart') }}\n}\n\n\nThe cart is simulated entirely in-memory. Each POST adds to the cart, and each GET returns the full list.\n\nStateful mocks turn the mock server into a simulation platform. This allows API stakeholders to have a realistic server. You can build full test workflows, mimic backend services, and let teams explore real behavior — before writing a single line of backend code.\n\n\n\nMocking is about realism and developer speed, not just sending  200 OK with dummy JSON. Enabling stakeholders quickly is a key success metric for Developer Experience (DX). If you’re looking to improve developer onboarding, shift QA left, or remove test dependency bottlenecks, consider adding a mock server to your API documentation. This is a simple yet powerful solution that doesn’t require managing infrastructure, writing scripts, or defining examples by hand."
        },
        {
          "id": "guides-openapi-code-first-spring",
          "title": "Generating OpenAPI docs for Java with Spring Boot",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "OpenAPI",
          "tags": "",
          "url": "/guides/openapi/code-first-spring/",
          "content": "API Code-first is the art of building an API, and then popping some annotations\nor metadata in there to output API documentation in an API description format\nlike OpenAPI.\n\nThe most popular API Code-first approach in Spring uses a tool called\nSpringdoc, which can generate OpenAPI v3.1 from any\nSpring-based application (Boot, Web MVC, Webflux, etc). A basic skeleton will be\ngenerated with absolutely no work whatsoever, then adding annotations will keep\nimproving the usefulness of the generated OpenAPI.\n\nPrerequisites\n\nTo follow this guide, you will need:\n\n\n  Java 21 or later\n  Maven 3.5+ or Gradle 7.5+\n  A Spring Boot application, which you can create by following the Spring Boot REST Service guide or cloning down our sample code.\n\n\nHere’s a few commands to help you out.\n\n# check for java. v21 or above is fine\njava --version\n\n# check if maven or gradle are there\nwhich mvn || which gradle\n\n\nIf you are new to working with Java applications it’s recommended to use an IDE\nlike IntelliJ IDEA or VS\nCode (using this great guide to running Spring\nin VS Code). You can use the\ncommand line if you prefer.\n\nCreating OpenAPI with Springdoc\n\nStep 1: Install the Springdoc dependency\n\nIf you are using Maven, add the following dependency to your pom.xml file:\n\n  &lt;dependency&gt;\n    &lt;groupId&gt;org.springdoc&lt;/groupId&gt;\n    &lt;artifactId&gt;springdoc-openapi-starter-webmvc-api&lt;/artifactId&gt;\n    &lt;version&gt;2.8.8&lt;/version&gt;\n  &lt;/dependency&gt;\n\n\nIf you are using Gradle, add this to your build.gradle file:\n\ndependencies {\n    implementation 'org.springdoc:springdoc-openapi-starter-webmvc-api:2.8.8'\n}\n\n\nThe springdoc-openapi-starter-webmvc-api package supports Spring MVC and\nSpring Boot, but for Spring Webflux support use the\nspringdoc-openapi-starter-webflux-api package instead.\n\nStep 2: Configure Springdoc\n\nYou can customize the OpenAPI generation by adding properties to your\napplication.properties or application.yml file.\n\n# application.properties\nspringdoc.api-docs.path=/openapi\n\n\nOr if you are using YAML:\n\n# application.yml\nspringdoc:\n  api-docs:\n    path: /openapi\n    title: My API\n    version: 1.0.0\n\n\nThe path setting defines where the OpenAPI description will be available.\nSpringdoc makes it available on http://localhost:8080/v3/api-docs by default,\nwith the v3 being a reference to OpenAPI v3.x, but you can change it to\nsomething more suitable for your application.\n\nWhy not a classy /openapi path so the OpenAPI description can be hosted on a\nnice clean http://localhost:8080/openapi once the application is running.\n\nStep 3: Create/find some REST controllers\n\nThis step is less of a step, and more “have some controllers and models” which\nyou probably already do. Whether you are starting from scratch or looking at an\nexisting application, let’s use the example of a EmployeeController, using\nHibernate and Spring Data JPA to make life a little easier.\n\n# src/main/java/com/bumpsh/demo/EmployeeController.java\nimport java.util.List;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PathVariable;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\nclass EmployeeController {\n\n  private final EmployeeRepository repository;\n\n  EmployeeController(EmployeeRepository repository) {\n    this.repository = repository;\n  }\n\n  @GetMapping(\"/employees\")\n  List&lt;Employee&gt; all() {\n    return repository.findAll();\n  }\n\n  @PostMapping(\"/employees\")\n  Employee newEmployee(@RequestBody Employee newEmployee) {\n    return repository.save(newEmployee);\n  }\n  \n  @GetMapping(\"/employees/{id}\")\n  Employee one(@PathVariable Long id) {\n    return repository.findById(id)\n      .orElseThrow(() -&gt; new EmployeeNotFoundException(id));\n  }\n\n  // snipped PUT and DELETE for now\n}\n\n\nThe repository is wrapping around the model to handle some tedious database\ninteraction, and the model looks like this:\n\n# src/main/java/com/bumpsh/demo/Employee.java\nimport java.util.Objects;\n\nimport jakarta.persistence.Entity;\nimport jakarta.persistence.GeneratedValue;\nimport jakarta.persistence.Id;\n\n@Entity\nclass Employee {\n\n  private @Id\n  @GeneratedValue Long id;\n  private String name;\n  private String role;\n\n  // snip constructor, getters and setters\n}\n\n\nSo far we’ve done absolutely nothing to this application code specific to\nSpringdoc or OpenAPI, but this is already enough for Springdoc to generate a\nbasic OpenAPI description.\n\nStep 4: Run the application\n\nBoot up the Spring Boot application to install all the dependencies and start\nthe HTTP server.\n\nIf you are using an IDE, you can run the application directly from there. For\nexample, in IntelliJ IDEA, you can right-click on the main class (the one\nannotated with @SpringBootApplication) and select “Run”.\n\nIf you are using Maven, run:\n\nmvn spring-boot:run\n\n\nIf you are using Gradle, run:\n\n./gradlew bootRun\n\n\nOnce the application is running and the HTTP server is available, the OpenAPI\ndescription will be available at http://localhost:8080/openapi for JSON, or\nhttp://localhost:8080/openapi.yaml for YAML.\n\n\n\nVery basic, and it’s only saying “what” which misses out on the critical “why”\nand “how” aspect of API documentation, but it will be a start.\n\nStep 5: Deploy to Bump.sh\n\nBefore we get stuck into making this OpenAPI perfect, lets get used to deploying\nit to Bump.sh so we can see how things look. Iterative improvements are easy\nwith Bump.sh, as it can be hooked up to deploy a new version of the\ndocumentation every time a pull request is merged in.\n\nIf you’re new to Bump.sh, create your first\nAPI. Then retrieve the\nname and token of this documentation from the CI deployment settings page.\nWe’ll use this to deploy the OpenAPI description with the Bump.sh CLI.\n\nnpm install -g bump-cli\n\n\nNow we can deploy the OpenAPI description to Bump.sh using the bump deploy command.\n\n$ bump deploy http://localhost:8080/openapi \\\n  --doc spring-code-first \\\n  --token my-documentation-token\n\n* Your new documentation version will soon be ready at https://bump.sh/bump-examples/hub/code-samples/doc/spring-code-first\n\n\nThis can then be automated in your CI/CD pipeline, so every time you merge a\npull request, the OpenAPI description is automatically deployed to Bump.sh.\nWe’ll show you how to do that in the end, but for now, let’s focus on improving\nthe OpenAPI description.\n\nStep 6: Improve OpenAPI with Java annotations\n\nThe skeleton OpenAPI description generated by Springdoc is a start, but it lacks\ncontext and explanations. To make it more useful, we can add annotations to the\ncontrollers and models providing more context to how and why this API works the\nway it does.\n\nThis is done with annotations from the io.swagger.v3.oas.annotations package,\nwhich allows you to add metadata around the Java source code. The Spring\nframework is already making clear which HTTP methods and URL paths are being\nused, so the job of these new annotations is to focus on the rest of it:\nparameters, request body, responses, status codes, and adding useful\ndescriptions and examples wherever possible.\n\nLet’s look at some examples of how to do this.\n\nDescribing Operation\n\nAdding a summary and description to the operations in the controller is an important first move, because the summary is used to generate the navigation menu, and the description is used to explain what the operation does in more detail.\n\n# src/main/java/com/bumpsh/demo/EmployeeController.java\nimport io.swagger.v3.oas.annotations.Operation;\n\n@RestController\nclass EmployeeController {\n\n  // Add the @Operation annotation to the method\n  @GetMapping(\"/employees/{id}\")\n  @Operation(summary = \"Get an employee by ID\", description = \"Returns the details of an employee based on the provided ID\")\n  List&lt;Employee&gt; all() {\n    return repository.findAll();\n  }\n\n  // Do this for each HTTP method in the controller\n  @PostMapping(\"/employees\")\n  @Operation(summary = \"Create a new employee\", description = \"Adds a new employee to the system\")\n  Employee newEmployee(@RequestBody Employee newEmployee) {\n    return repository.save(newEmployee);\n  }\n}\n\n\nJust adding summary and description to the operation is a solid first step to\nmaking your OpenAPI documentation more useful and interesting to look at. See\nthe OpenAPI YAML description by Springdoc after adding these annotations.\n\nopenapi: 3.1.0\ninfo:\n  title: OpenAPI definition\n  version: v0\nservers:\n- url: http://localhost:8080\n  description: Generated server url\npaths:\n  /employees/{id}:\n    get:\n      tags:\n      - Employee Management\n      summary: Get an employee by ID\n      description: Returns the details of an employee based on the provided ID\n      operationId: one\n      parameters:\n      - name: id\n        in: path\n        required: true\n        schema:\n          type: integer\n          format: int64\n      responses:\n        \"200\":\n          description: OK\n          content:\n            '*/*':\n              schema:\n                $ref: \"#/components/schemas/Employee\"\n\n\nOf course these example operations are very basic, but this is a good place to\nget stuck into letting users know what to expect when they call your API. Is the\nresponse paginated? Is there a default sort order?\n\n\n\nTagging Controllers\n\n# src/main/java/com/bumpsh/demo/EmployeeController.java\n\nimport io.swagger.v3.oas.annotations.tags.Tag;\n\n// Add the @Tag annotation to the class\n@RestController\n@Tag(name = \"Employee Management\", description = \"Operations related to employee management\")\nclass EmployeeController {\n  // ... existing code ...\n}\n\n\nAdding Parameters\n\nParameters can be added to the method using the @Parameter annotation. This is\nuseful for describing path parameters, query parameters, and request body\nparameters.\n\nThese parameter annotations are applied to the method parameters, like this:\n\n# src/main/java/com/bumpsh/demo/EmployeeController.java\nimport io.swagger.v3.oas.annotations.Parameter;\n\n@RestController\nclass EmployeeController {\n\n  @GetMapping(\"/employees/{id}\")\n  @Operation(summary = \"Find an employee\", description = \"Returns the details of an employee based on the provided ID\")\n  public Employee one(\n      @Parameter(in = ParameterIn.PATH, description = \"ID of the employee to retrieve\", required = true, schema = @Schema(type = \"integer\")) \n      @PathVariable Long id) {\n    // ... existing code ...\n  }\n}\n\n\nThis will add a parameter to the OpenAPI description for the id path variable,\nwhich looks like this in the generated OpenAPI:\n\npaths:\n  '/employees/{id}':\n    get:\n      parameters:\n        - name: id\n          in: path\n          required: true\n          description: ID of the employee to retrieve\n          schema:\n            type: integer\n            format: int64\n\n\nParameter descriptions can contain all sorts of handy information, like letting people know that a date parameter is in ISO 8601 format, or that a\nstring parameter is a UUID. This is useful for consumers of the API to understand\nhow to use the API correctly without having to look at the code or ask questions.\n\n\n\nAdding Responses\n\nHow about adding a response schema to the @ApiResponse annotation to describe\nwhat the response body will look like? By default, Springdoc will use the return\ntype of the method to generate the response schema, but you can override this by\nusing the @ApiResponse annotation.\n\n# src/main/java/com/bumpsh/demo/EmployeeController.java\n\nimport io.swagger.v3.oas.annotations.responses.ApiResponse;\nimport io.swagger.v3.oas.annotations.media.Content;\nimport io.swagger.v3.oas.annotations.media.Schema;\n@RestController\nclass EmployeeController {\n\n  @GetMapping(\"/employees/{id}\")\n  @Operation(summary = \"Find an employee\", description = \"Returns the details of an employee based on the provided ID\")\n  @ApiResponse(\n    responseCode = \"200\", \n    description = \"Successful operation\", \n    content = @Content(mediaType = \"application/json\", \n    schema = @Schema(implementation = Employee.class))\n  )\n  public Employee one(@PathVariable Long id) {\n    // ... existing code ...\n  }\n\n\nAnnotating the Model\n\nTo make the OpenAPI description even more useful, you can annotate the model\nclass to describe its properties. This is done using the @Schema annotation.\n\n# src/main/java/com/bumpsh/demo/Employee.java\nimport jakarta.persistence.Entity;\nimport jakarta.persistence.GeneratedValue;\nimport jakarta.persistence.Id;\n\nimport io.swagger.v3.oas.annotations.media.Schema;\n\n@Entity\n@Schema(description = \"Employees are individuals who work for an organization, \" +\n  \"contributing their skills and expertise to achieve the organization's goals.\" +\n\n  \"Roles are the specific functions or positions that employees occupy within the organization, \" +\n  \"defining their duties, responsibilities, and the scope of their work.\"\n)\nclass Employee {\n\n  private @Id\n\n  @Schema(accessMode = Schema.AccessMode.READ_ONLY, description = \"Unique identifier which should not be shared publicly\", example = \"1234\")\n  @GeneratedValue Long id;\n  \n  @Schema(description = \"Employee name\", example = \"Bilbo Baggins\")\n  private String name;\n\n  @Schema(description = \"Employee role\", example = \"Software Engineer\")\n  private String role;\n\n  Employee() {}\n\n  Employee(String name, String role) {\n    this.name = name;\n    this.role = role;\n  }\n\n  // Getters and Setters\n}\n\n\nThis will add a description to the Employee model, and also describe each\nproperty with its type, description, and example value. The generated OpenAPI\nwill look like this:\n\ncomponents:\n  schemas:\n    Employee:\n      type: object\n      description: \"Employees are individuals who work for an organization, contributing\\\n        \\ their skills and expertise to achieve the organization's goals.Roles are\\\n        \\ the specific functions or positions that employees occupy within the organization,\\\n        \\ defining their duties, responsibilities, and the scope of their work.\"\n      properties:\n        id:\n          type: integer\n          format: int64\n          description: Unique identifier which should not be shared publicly\n          example: 1234\n          readOnly: true\n        name:\n          type: string\n          description: Employee name\n          example: Bilbo Baggins\n        role:\n          type: string\n          description: Employee role\n          example: Software Engineer\n\n\nThis will make the OpenAPI description much more useful, as it provides a clear\nunderstanding of the model and its properties, including their types, descriptions,\nand example values.\n\nOver time as properties change, the OpenAPI description will also change,\nallowing consumers of the API to always have an up-to-date understanding of the\nAPI and its models.\n\nStep 7: Deploy the improved OpenAPI description\n\nNow that we’ve added some annotations to the controllers and models, we can\ngenerate a new OpenAPI description and deploy it to Bump.sh.\n\nRun the application again to generate the new OpenAPI description, and then\ndeploy it to Bump.sh using the bump deploy command.\n\n$ bump deploy http://localhost:8080/openapi \\\n  --doc spring-code-first \\\n  --token my-documentation-token\n* Your new documentation version will soon be ready at https://bump.sh/bump-examples/hub/code-samples/doc/spring-code-first\n\n\nThis will update the OpenAPI description on Bump.sh with the new version, which\nwill now include the additional context and explanations we added with the\nannotations.\n\nStep 8: Automate the deployment\n\nTo automate the deployment of the OpenAPI description to Bump.sh, you can add a\nstep in your CI/CD pipeline to run the bump deploy command every time a pull\nrequest is merged. This way, the OpenAPI description will always be up-to-date\nand available on Bump.sh.\n\nYou can use a CI/CD tool like GitHub Actions, GitLab CI, or Jenkins to run the\ndeployment command automatically.\n\nFor example, if you are using GitHub Actions, you can create a workflow file\n.github/workflows/bump.yml with the following content:\n\n\nname: Check &amp; deploy API documentation\npermissions:\n  contents: read\n  pull-requests: write\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    branches:\n      - main\njobs:\n  deploy-doc:\n    if: ${{ github.event_name == 'push' }}\n    name: Deploy API documentation on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Set up JDK\n        uses: actions/setup-java@v2\n        with:\n          distribution: 'temurin'\n          java-version: '21'\n\n      - name: Build and run the application\n        run: |\n          mvn clean spring-boot:run &amp;\n\n      - name: Wait for the application to start\n        run: sleep 30\n\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: spring-code-first\n          token: ${{secrets.BUMP_TOKEN}}\n          file: http://localhost:8080/openapi\n\n\nThis workflow will run every time a commit is pushed to the main branch, build\nthe application, wait for it to start, and then deploy the OpenAPI description\nto Bump.sh using the bump deploy command.\n\nMake sure to set the BUMP_TOKEN secret in your GitHub repository settings with\nthe token you retrieved from the Bump.sh CI deployment settings page.\n\nSample Code\n\nThe sample code for this guide is published on GitHub so you can try that if\nyou’re having trouble adding it to your application:\nspring-code-first, and\nthe deployed\ndocumentation\nis over here."
        },
        {
          "id": "guides-openapi-design-first-spring",
          "title": "Using OpenAPI to simplify building and testing Java and Spring APIs",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "OpenAPI",
          "tags": "",
          "url": "/guides/openapi/design-first-spring/",
          "content": "Spring Boot is a powerful Java framework with loads of handy community\nextensions for building APIs, and working with\nOpenAPI and all its brilliant\ntooling.\n\nSome folks may be used to the API code-first workflow, where you write the whole\nAPI then sprinkle in some metadata later using Swagger Annotations or something\nsimilar. The API design-first workflow is the opposite of that approach, and\nhelps you write less code whilst also getting better quality testing and 100%\naligned API documentation.\n\nInstead of rushing into the code, we can build OpenAPI descriptions before\nwriting any code at all, like creating a blueprint before building a house. Once\nyou have the OpenAPI description documents saved (ideally in your source code\nrepository), you can leverage it at every step of the API lifecycle, to produce\nmock APIs for clients\nto test assumptions with, build client libraries without writing any\ncode, make\nreally effective contract testing, and even generate backend code to get the\napplication teams started once the contract is all signed off.\n\nThe whole API design first workflow is huge depending on how much of its power\nyou leverage, but this guide is going to look at how to parts of the workflow\nwork with Spring Boot: server-side request validation, and contract testing.\n\n\n  Getting OpenAPI \\&amp; Bump.sh Setup\n  Request Validation with OpenAPI in Spring Boot\n  Contract Testing Spring APIs with OpenAPI\n  Sample Code\n\n\nGetting OpenAPI &amp; Bump.sh Setup\n\nThe API design-first workflow means you’ll need to create your OpenAPI\ndescription before you start writing all your code, so if you don’t have an\nopenapi.yaml already that is probably the first step. You can use a wide\nvariety of graphical editors, text\neditors, or traffic\nsniffing, or\nAI to generate this OpenAPI, and you can use our\nultimate OpenAPI\nGuide\nto help you get started writing and updating OpenAPI.\n\nIf you are switching from code first and getting on the design first train, you\ncan export your old annotations-based\nOpenAPI, download it, save it as\nopenapi.yaml, stick it through OpenAPI\nFormat to tidy it up a little,\nand delete all the old annotations from cluttering your codebase. You don’t need\nthose annotations anymore, we’re gonna work with openapi.yaml directly and use\nthat as our source of truth for everything.\n\nAlternatively you can grab some sample OpenAPI from the API Guru\nmarketplace, and click JSON or YAML to download their\nOpenAPI descriptions.\n\nOnce you have an OpenAPI description document, pop it into your Git repository.\nThis is often in the root as openapi.yaml.\n\nBuilding an API for a bunch of clients is always a tricky one, but by deploying\nthe documentation first, you can see if people like the look of the API before\nyou waste time building it.\n\nThen, as you progress through, especially if you add these tools to an existing\ncodebase, you will continue to find mistakes in your OpenAPI or your actual API\ncode. Improve both as you go until you have a perfect match that will never\nagain be broken, solving the “docs vs code” drift problem, and every fix will be\ndeployed to Bump.sh with each commit/merge.\n\n$ bump deploy openapi.yaml \\\n  --doc spring-design-first \\\n  --token my-documentation-token\n\n* Your new documentation version will soon be ready at https://bump.sh/hub/code-samples/doc/spring-design-first\n\n\nInstead of using the CLI you\ncould use GitHub\nActions,\nor a bunch of other Continuous Integration.\n\nOnce Bump.sh is hooked up, let’s look at how we’d teach a Spring Boot API (new,\nor existing) to handle request validation for us.\n\nRequest Validation with OpenAPI in Spring Boot\n\nIn a Spring Boot application, you can use OpenAPI to validate incoming requests\nagainst the API description. This ensures that the requests conform to the\nexpected structure and data types defined in the OpenAPI specification.\n\nTo demonstrate this, let’s assume we have a simple Spring Boot application with\nan Employee entity that has two properties: name and role. We’ll create a\ncontroller that handles incoming requests to create new employees, but it\ndoesn’t do any validation just yet.\n\n@RestController\nclass EmployeeController {\n\n\tprivate final EmployeeRepository repository;\n\n\tEmployeeController(EmployeeRepository repository) {\n\t\tthis.repository = repository;\n\t}\n\n\t@GetMapping(\"/employees\")\n\tList&lt;Employee&gt; all() {\n\t\treturn repository.findAll();\n\t}\n\n\t@PostMapping(\"/employees\")\n\tEmployee newEmployee(@RequestBody Employee newEmployee) {\n\t\treturn repository.save(newEmployee);\n\t}\n\n\nWhen a request is made it will try and create a record with name and role,\nbut if either property is missing (or misspelled) it will just save it anyway.\n\ncurl -iXPOST -H 'content-type:application/json' \\\n  localhost:8080/employees \\\n  --data '{\"role\":\"thief\",\"nam\":\"Bilbo\"}'\n\n\nHTTP/1.1 200\nContent-Type: application/json\n\n{\"id\":3,\"name\":null,\"role\":\"thief\"}\n\n\nOops!\n\nThis API needs more validation.\n\nBringing OpenAPI into the Spring Boot Application\n\nIf we’re going to teach Spring Boot and OpenAPI to get along, let’s first move OpenAPI somewhere Spring Boot is expecting it and de-clutter our root in the process. A great place for this is the src/main/resources/openapi/ directory.\n\nmkdir -p src/main/resources/openapi\nmv openapi.yaml src/main/resources/openapi/openapi.yaml\n\n\nInstalling Kappa\n\nKappa is a library that integrates OpenAPI with Spring Boot, allowing you to validate requests against your OpenAPI description without needing to generate code. It acts as a middleware that intercepts requests and checks them against the OpenAPI schema before they reach your controllers.\n\nKappa can be installed using the kappa-spring dependency, which provides tight integration with Spring Boot and removes the need for us to create our own HTTP middleware.\n\nTo install kappa-spring with Maven, pop it into pom.xml.\n\n  &lt;dependency&gt;\n    &lt;groupId&gt;com.github.erosb&lt;/groupId&gt;\n    &lt;artifactId&gt;kappa-spring&lt;/artifactId&gt;\n    &lt;version&gt;2.0.0-RC16&lt;/version&gt;\n  &lt;/dependency&gt;\n\n\nConfiguring Kappa in Spring Boot\n\nNow that Kappa is installed, we need to enable it in our Spring Boot application. Kappa provides an @EnableKappaRequestValidation annotation to stick on our Application class, then we can point Kappa to our OpenAPI in the kappaSpringConfiguration() method:\n\npackage com.bumpsh.demo;\n\nimport com.github.erosb.kappa.autoconfigure.EnableKappaRequestValidation;\nimport com.github.erosb.kappa.autoconfigure.KappaSpringConfiguration;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.context.annotation.Bean;\nimport java.util.LinkedHashMap;\n\n@SpringBootApplication\n@EnableKappaRequestValidation\npublic class DemoApplication {\n\n\tpublic static void main(String[] args) {\n\t\tSpringApplication.run(DemoApplication.class, args);\n\t}\n\n\t@Bean\n\tpublic KappaSpringConfiguration kappaSpringConfiguration() {\n\t\tKappaSpringConfiguration kappaConfig = new KappaSpringConfiguration();\n\t\tvar pathPatternToOpenapiDescription = new LinkedHashMap&lt;String, String&gt;();\n\t\tpathPatternToOpenapiDescription.put(\"/**\", \"/openapi/openapi.yaml\");\n\t\tkappaConfig.setOpenapiDescriptions(pathPatternToOpenapiDescription);\n    kappaConfig.setValidationFailureSender(ValidationFailureSender.rfc9457Sender());\n\t\treturn kappaConfig;\n\t}\n\n}\n\n\nThat is basically it. With a surprisingly little amount of work, our application is now protected with a middleware that will reject any invalid requests.\n\nI know, it seems too easy, so let’s test it out!\n\nTesting the Middleware\n\nLet’s try sending a request that is missing the name property, which is required in the OpenAPI description.\n\ncurl -iXPOST -H 'content-type:application/json' \\\n  localhost:8080/employees \\\n  --data '{\"role\":\"thief\"}'\n\n\nWhen we run this request, we should see a response like this:\n\nHTTP/1.1 400\nContent-Type: application/problem+json\nConnection: close\n\n{\n  \"type\" : \"https://erosb.github.io/kappa/request-validation-failure\",\n  \"status\" : 400,\n  \"title\" : \"Validation failure\",\n  \"detail\" : \"Invalid request.\",\n  \"errors\" : [\n    {\n      \"message\" : \"required properties are missing: name\",\n      \"dataLocation\" : \"$request.body (line 1, position 1)\",\n      \"dynamicPath\" : \"#/$ref/required\"\n    }\n  ]\n}\n\n\nThe error that comes is super useful and contains a whole lot of information to help humans and computers move forward.\n\nThe format if this error is using RFC 9457: Problem Details for HTTP APIs, so this may be familiar to you. You can design your own API to use these same error responses, and even have multiple APIs having all the same formats, so everything works nicely and consistently.\n\nProblem Details cover the main keywords here: type, status, title, detail, which help to explain what’s going on. This is a HTTP 400 (Bad Request), and the specific problem as that it’s got an invalid request, as determined by https://erosb.github.io/kappa/request-validation-failure which is a unique error as defined by the tool Kappa. Then to help explain the validation issue further, Kappa adds in the errors array, which includes an object with details of the validation failure.\n\nIn this instance it’s let us know that name is missing from the request body, so we can go ahead and add that back in.\n\ncurl -iXPOST -H 'content-type:application/json' \\\n  localhost:8080/employees \\\n  --data '{\"role\":\"thief\",\"name\":\"Bilbo\"}'\n\n\nHTTP/1.1 200\nContent-Type: application/json\n\n{\"id\":4,\"name\":\"Bilbo\",\"role\":\"thief\"}\n\n\nThere we go! The request is now valid, and the response is a HTTP 200 OK with the created employee record.\n\nContract Testing Spring APIs with OpenAPI\n\nContract testing being built from from OpenAPI is one of the best things about using the API design-first workflow, because it allows us to ensure the API is working exactly as planned all the way through from planning and development to deployment.\n\nInstead of redefining the contract in the test suite and having that differ slightly from the documentation or the validation, everything is all using the same OpenAPI which is sat right there in the codebase.\n\nEnabling contract testing to make sure requests and responses match the OpenAPI does not involve any new tools, because Kappa handles this too. No new concepts or workflows will be required, we can just use the existing Spring Boot testing framework and add a single annotation to our tests to enable contract testing.\n\n  &lt;dependency&gt;\n    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;\n    &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;\n    &lt;scope&gt;test&lt;/scope&gt;\n  &lt;/dependency&gt;\n\n\nNow we can write a test that will validate the API against the OpenAPI description. Here’s an example of a test that checks the /employees endpoint:\n\npackage com.bumpsh.demo;\n\nimport com.github.erosb.kappa.autoconfigure.EnableKappaContractTesting;\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.http.MediaType;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders;\n\nimport static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;\nimport static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;\n\n/**\n * The @EnableKappaContractTesting will tell Kappa to validate all HTTP requests and responses against the openapi.yaml.\n *\n * So contract testing is an additional verification step in every test. If an endpoint sends a response that doesn't\n * match the openapi description, then the test will fail, even if all assertions of the concrete test would pass.\n */\n@SpringBootTest\n@AutoConfigureMockMvc\n@EnableKappaContractTesting\n@Tag(\"failing-contract-tests\")\npublic class EmployeeApiTest {\n\n    @Autowired\n    MockMvc mvc;\n\n    /**\n     * Fails because the openapi.yaml describes a HTTP 201 Created response code, but the SUT responds with 200 OK.\n     */\n    @Test\n    void responseCodeMismatch() throws Exception {\n        mvc.perform(post(\"/employees\").contentType(MediaType.APPLICATION_JSON)\n                .content(\"\"\"\n                        {\n                            \"name\": \"Bilbo\",\n                            \"role\": \"ring-bearer\"\n                        }\n                        \"\"\"))\n                // here we don't exactly specify the expected response code\n                // but due to @EnableKappaContractTesting, Kappa will still check if the response code is in the openapi.yaml\n                .andExpect(status().is2xxSuccessful());\n    }\n\n    /**\n     * Fails because the 404 response in the openapi.yaml describes a mandatory \"id\" property, but it isn't present in the response\n     */\n    @Test\n    void notFoundResponseBodyMismatch() throws Exception {\n        mvc.perform(MockMvcRequestBuilders.get(\"/employees/22\").accept(MediaType.APPLICATION_JSON))\n                .andExpect(status().isNotFound())\n                // actually, this is the json that will be returned by the endpoint, but it doesn't match the openapi description because of the missing \"id\" property\n                .andExpect(content().json(\"\"\"\n                        {\n                            \"message\": \"Could not find employee 22\"\n                        }\n                        \"\"\"));\n    }\n}\n\n\nThis test is a pretty standard spring MockMvc test, with an additional @EnableKappaContractTesting annotation. This will tell Kappa to validate all requests and responses against the openapi.yaml description, and make the tests fail on mismatch.\n\nmvn test\n\n\nWhen you run the tests, Kappa will validate the requests and responses against the OpenAPI description. If there are any mismatches, the tests will fail, providing detailed information about what went wrong.\n\nA common error for a contract test is a test not sending a required field. That mismatch could be down to a field only recently being made required (which could be a BC break), or OpenAPI falsely describing a field as required when it is not. Either way, the test will fail and provide a useful error message to help you fix the mismatch.\n\n[ERROR]   EmployeeApiTest.responseCodeMismatch:37 Invalid request.\nrequired properties are missing: thirdThing\ninstance location: $request.body (line 1, position 1)\nschema location: target/classes/openapi/openapi.yaml#/components/schemas/Employee/required\n\tevaluated on dynamic path: target/classes/openapi/openapi.yaml#/$ref/required\n\n\nThis error message indicates that the request body is missing a required property thirdThing, which is defined in the OpenAPI description. The test will fail, and you can then go back to your API implementation to fix the issue.\n\nAnother common problem contract testing helps surface is content type or response code mismatches. For example, if the OpenAPI description specifies that a 404 response should return a JSON body with a title and detail property like RFC 9457, but the actual response is a plain text message.\n\n[ERROR] Failures:\n[ERROR]   EmployeeApiTest.notFoundResponseBodyMismatch:54 Invalid response.\ncould not parse HTTP entity: unexpected character C\ninstance location: $request.body (line 1, position 1)\nschema location: target/classes/openapi/openapi.yaml#/paths/~1employees~1{id}/get/responses/404/content\n\n\nHaving the OpenAPI description (and therefore API documentation) show JSON errors but sending plain text errors would be embarrassing and problematic, but Kappa caught this mismatch and failed the test. OpenAPI-based contract testing tools are not just looking at the shape of the data being returned, but the whole response, including the content type and status code.\n\nThis is a great way to ensure that your API is always in sync with the OpenAPI description, and that any changes to the API are reflected in the tests.\n\nThis approach allows us to ensure that our API is always in sync with the OpenAPI description, and that any changes to the API are reflected in the tests.\n\nSample Code\n\nBy integrating OpenAPI into our Spring Boot application with Kappa, we can easily validate requests and responses against the OpenAPI description without needing to generate code. This not only simplifies the development process but also ensures that our API is robust and reliable.\n\nTo help you get Kappa into your Spring Boot codebase, check out our sample code published on GitHub. Please take a look at spring-design-first, and the deployed documentation is over here."
        },
        {
          "id": "guides-openapi-openapi-format-overlays",
          "title": "OpenAPI Format: A GUI for Overlays",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "OpenAPI",
          "tags": "",
          "url": "/guides/openapi/openapi-format-overlays/",
          "content": "Overlays for OpenAPI and AsyncAPI are all the rage, helping all sorts of users improve API descriptions by “patching” in extra content. Tech writers improving bare bones OpenAPI with descriptions and tags to improve the resulting API documentation. Developers are popping code samples for generated SDKs in instead of relying on generic samples. The utility of this specification seems limitless, but overlays come with a steep learning curve.\n\nThe OpenAPI Format Playground aims to make things easier with a graphical interface on building and applying overlays, so let’s take a look at how it works.\n\nWhat is OpenAPI Format\n\nOpenAPI Format started life as a NPM command-line tool, with the goal of helping to tidy up OpenAPI documents. It can remove unused components, sort various components and parameters alphabetically, upgrade documents from OpenAPI v3.0 to v3.1, and all kinds of other handy utilities to keep OpenAPI documents neat and tidy.\n\nRecently OpenAPI Format got the ability to apply overlays to OpenAPI documents, but they didn’t stop with a command-line interface for this.\n\nIntroducing OpenAPI-Format Playground\n\nThe maintainers of OpenAPI-Format built a Playground, a web interface to help folks work with overlays in a visual way.\n\nOverlays are essentially a series of “actions” which can be applied to a document, in order to create a new document with the resulting changes. Actions need a target defined as a JSONPath to point to a particular part of an OpenAPI document, and an action type of of “update” or “remove”. These actions can be used in combination to add new content, replace content, or filter out bits that aren’t wanted.\n\nFiguring out how to do all of this just from staring into an empty text editor can be a bit rough, so let’s try using the playground to guide us on our way.\n\nCreating Overlays with OpenAPI-Format Playground\n\nStep 1 - Add an OpenAPI document\n\nCopy in some OpenAPI into the box on the left. If you don’t have any handy why not use the Train Travel API by Bump.sh.\n\n\n\nStep 2 - Open the Overlay section\n\nClick the “OpenAPI Overlay” button and it will go to a different section. In here you can give your Overlay a name, something that describes what we are trying to do.\n\nFor example: “Add descriptions to tags” is pretty clear about what is going to be achieved by the actions of this overlay. The version number can be whatever.\n\n\n\nStep 3 - Play with JSONPath\n\nJSONPath is hard and scary at first, but much like RegEx it can be mastered, especially with the help of a handy tool like this.\n\nTo start with JSONPath you use the $ as the root, then . to go to a property. The OpenAPI structure can be navigated like this, so something like $.info $.paths $.tags or $.components is a good start.\n\nWhen you type in a JSONPath in the Target field, the Target Preview will update, so you can be certain you are working on the right bit of the OpenAPI document.\n\n\n\nTo update a specific tag, JSONPath supports filters, which use a ?(condition) syntax. Here I am looking through an array of objects for the one which has a name property equal to “Stations”: $.tags[?(@.name == \"Stations\")].\n\n\n\nThe JSONPath worked, I can see the tag object is there in the target preview. Now to work on the action.\n\nStep 4 - Create the first Action\n\nOverlay actions come in two flavours: “update” or “remove”.\n\nUpdate is basically a merge, taking the value provided and updating the target to include these values. If it’s an object it will add in the properties provided and override any that were there with the same name.\n\nTo add/override the description for a specific tag, we can pass a new description property, along with any other properties from the tags object.\n\n\n\nRemove will not need to take a value, it just removes the target object entirely. For example, removing the tag Payments.\n\n\n\nThere’s a whole lot more than can be done than just mess with tags, but instead of getting stuck into advanced overlaying, let’s see how these two actions run.\n\nStep 5 - Compare the output\n\nClick the “Apply Overlay” button and it’s back to the main Playground screen.\n\n\n\nThe main screen has OpenAPI input on the left and OpenAPI output on the right. The version on the right will have had the overlay applied as well as any other formatting rules, so we should see two changes to the tags: object:\n\n\n  Stations has a wonderful new description.\n  The Payments tag has vanished entirely.\n\n\n\n\nIf its hard to see what has changed and you really want to be sure, you can click the Show Diff button to see the difference between the original OpenAPI document on the left, and the one with overlay changes on the right. Keep in mind that this will also show formatting changes, so there might be more changes than just the ones you made with the overlay.\n\n\n\nNow you can keep iterating on the overlays you want to build, keep trying them out in this playground, then when its working nicely go back to the Overlay screen and click “Download Overlay”.\n\nBonus: Editing/Debugging Overlays\n\nOnce overlays are out and about in the world they will sometimes need improving or debugging to make sure everything works as expected.\n\nOpenAPI Format will allow you to import an Overlay from a file or a URL, and you could even “Switch to Code Mode” and paste the raw YAML of an overlay into the editor.\n\n\n\nOpenAPI Format is a brilliant tool for helping teams get started with overlays, without forcing them to stare into an empty text-editor and try to learn Overlays and JSONPath all at once. Technical writers and others can use Playground to create overlays to improve API documentation in countless ways. How will you use them?"
        },
        {
          "id": "guides-bump-sh-tutorials-documenting-multiple-api-versions",
          "title": "Documenting Multiple API Versions",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Bump-sh tutorials",
          "tags": "",
          "url": "/guides/bump-sh-tutorials/documenting-multiple-api-versions/",
          "content": "Versioning an API can be incredibly difficult, but working out how to handle documentation for multiple API versions can be even more of a challenge. When you release a new version of an API you’re going to need to keep the old one around for a while, and manage it as its own project until it can be retired. Thankfully Bump.sh makes it as simple and flexible as you could hope for.\n\nDifferent workflows\n\nFirst of all, where and how is the API source code stored?\n\n\n  one Git repository for each API version\n  one Git branch for each API version\n  one Git repository for all the API versions (monorepo)\n\n\nIt’s best practice to keep the API descriptions (e.g.: openapi.yaml) with the API source code, so whichever of the above you’re doing will work out just fine. If you’re generating OpenAPI or AsyncAPI from source code, that’ll work out just fine too.\n\nLet’s look at how API documentation version management works with Bump.sh, and walk through a few of these scenarios so you can get things set up however you happen to work.\n\nBump.sh “Branches”\n\nBump.sh supports branches as a concept, but this does not have to be specifically Git branches. It is simply a branch of the API, and that could be version numbers that match the API major version, it could be terms like “beta” and “next”. For example, the Twilio Chat API has v2 and v3 versions deployed, so they would set up a v2 branch and a v3 branch.\n\nTo add the v3 to the API documentation, go to the API settings on Bump.sh, click Branches, and add a new branch. Then pick which branch should be the default, which is the branch which will display to users before they pick a version.\n\n\n\nNow there are two branches, using Bump CLI deploy command will deploy to the default branch of the API. You can pick a specific branch by using the --branch= argument like so:\n\nbump deploy --doc \"twilio-chat\" \\\n  --token \"&lt;your-token&gt;\" \\\n  --branch v3 \\\n  v3/openapi.yaml\n\n\nAs changes are made to the old “v2 (Legacy)” branch they too can be deployed, but the name of the branch is a hyphenated version of the name, so “v2 (Legacy)” becomes “v2-legacy”.\n\nbump deploy --doc \"twilio-chat\" \\\n  --token \"&lt;your-token&gt;\" \\\n  --branch v2-legacy \\\n  v2/openapi.yaml\n\n\nThis example assumed that both versions of the API were in the same repository and used the CLI, but we can now look at a few more examples that might feel more familiar to your setup.\n\nExample: One directory per API version\n\nThis setup involves one GitHub repository, but each version of the API exists in a sub-directory. This means a single GitHub Action can be used, with two similar deployment steps so that both APIs can be deployed from the same workflow.\n\n# .github/workflows/bump.yml\nname: Deploy API documentation\non:\n  push:\n    branches: [main]\n\njobs:\n  deploy-doc:\n    if: ${{ github.event_name == 'push' }}\n    name: Deploy API documentation on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Deploy API v2 documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;your-doc-id-or-slug&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: src/v2/api/openapi.yaml\n          branch: v2-legacy\n\n      - name: Deploy API v3 documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;your-doc-id-or-slug&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: src/v3/api/openapi.yaml\n          branch: v3\n\n\nExample: One Git branch per API version\n\nThis setup involves one GitHub repository. Each version of the API is\nin its own Git branch, a v2 and v3. In this instance, you could\nuse a GitHub Action, that gets triggered on each of the corresponding\nbranch. For more flexibility between the Git branch and the Bump.sh\ntarget file &amp; branch, we’ve detailed below configuration mapping that\nyou will need to adapt to your needs.\n\nThe workflow file would look like this in both Git branches:\n\n# .github/workflows/bump.yml\nname: Deploy API documentation pushes\non:\n  push:\n    branches: [v2, v3]\n\njobs:\n  deploy-doc:\n    name: Deploy API documentation on Bump.sh\n    runs-on: ubuntu-latest\n    env:\n      TARGET_BUMP_BRANCH: &gt;\n        ${{\n          fromJson('{\n            \"v3\": \"v3\",\n            \"v2\": \"v2-legacy\"\n          }')[github.ref_name]\n        }}\n      SOURCE_FILE: &gt;\n        ${{\n          fromJson('{\n            \"v3\": \"openapi.yaml\",\n            \"v2\": \"legacy/directory/structure/swagger.yaml\"\n          }')[github.ref_name]\n        }}\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;your-doc-id-or-slug&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: ${{env.SOURCE_FILE}}\n          branch: ${{env.TARGET_BUMP_BRANCH}}\n\n\nExample: One repository for each API version\n\nThis would look exactly the same as the previous example just pushing the workflow file to two different repos instead of two different branches. You would also need to adapt the branch name targets in the workflow file to align with the branches you use in each of the repository.\n\nBranch names\n\nThe branch names are important, and should be chosen carefully. The default branch is the one that will be shown to users when they first visit the API documentation. When there is only one branch there will be no need to pick a branch, but as soon as there are two or more branches the user will be prompted to pick a branch.\n\nSome API maintainers decide to name branches after major versions like v1, v2, v3. Some maintainers prefer branching based on release dates, especially when using API evolution or date-based versions.\n\nSome API maintainers prefer to use terms like “beta” and “next” for their branches, which can be useful when the API is in a state of flux and the version numbers are not yet stable.\n\nDeprecating old API versions\n\nWhen a new API version is created, and you create a new Bump.sh branch for the API documentation to match, at some point you will want new users to start using it.\n\nAnnouncements to users by email are a common soft-touch start, letting users know all the brilliant new functionality available in the new version of the API so they want to go and upgrade, but that won’t get them all, and we also want to make sure new users aren’t picking up the old version.\n\nThe “carrot” of dangling a new shiny default version works best when deployed alongside a stick, and for API versioning that stick is deprecation.\n\nThe basic premise of deprecation for APIs is giving people a heads up that an operation is going away and is best avoided. This would make a new API user think twice about using that operation, or that entire version if all the operations are deprecated, and helps point them towards the new version.\n\nBump.sh eases the pain of API deprecation in several ways, with automatic changelogs showing people what has changed, highlighting deprecations in the documentation, and alerting subscribers about meaningful changes."
        },
        {
          "id": "guides-api-basics-design-reviews",
          "title": "API Design Reviews Don’t Have to be Hard",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "API Basics",
          "tags": "",
          "url": "/guides/api-basics/design-reviews/",
          "content": "An API Design Review is one of a few parts of a larger API Governance program, where various people review changes to an API to make sure its a good choice for the architecture and wider ecosystem. This often involves a wide variety of persona, including API designers, API developers, technical writers, system architects, and maybe even a dedicated governance team.\n\nDesign reviews are generally done for an API overall when its first form is created, then with following iterations as new endpoints and other changes are made. Beyond the basic tire-kicking, a design review is important to ensure the API is well-designed, compliant with standards and style guides, meets the needs of its consumers, and does not introduce problems to the wider ecosystem.\n\nWhat happens during a design review\n\n\n  \n    Consistency and Standards Compliance - Ensure the API follows established\ndesign standards and conventions, covering naming conventions, Hypermedia\nformats, authentication strategies, etc.\n  \n  \n    Clarity - Verify that the API endpoints, parameters, and responses are\nclearly documented. Ensure descriptions are meaningful and useful for\ndevelopers of both the API and its consumers.\n  \n  \n    Functionality and Usability - Make sure the API endpoints provide the\nnecessary functionality, evaluating the ease of use for developers integrating\nwith the API.\n  \n  \n    Performance and Scalability - Evaluate the API design for potential\nperformance bottlenecks. Are consumers going to need to load 1,000 endpoints\nto solve the main use-case, or is data being computed on load which might slow\nthings down?\n  \n  \n    Backward Compatibility - Identify any breaking changes that could affect\nexisting consumers of the API. Ensure that changes are backward compatible or\nproperly versioned.\n  \n  \n    Forwards Compatibility - Is this change going to work properly and fit in\nwith future changes that are either planned to happen, or likely to happen\nwithin the wider ecosystem, to avoid conflicts or duplication.\n  \n  \n    Security and Privacy - Check that authentication and authorization\nmechanisms are present and appropriate for the task at hand, and\npersonal/private information is not being leaked by URL fiddling.\n  \n  \n    Error Handling and Responses - Ensure that error responses are sensible,\nfollowing the right standards, and are both meaningful and insightful for\ndevelopers and potentially any end-users who end up seeing the error messages\ndirectly.\n  \n  \n    Impact on Documentation - Determine how changes will affect existing\ndocumentation (reference docs, guiders, tutorials, etc). Ensure that\ndocumentation is updated to reflect any changes in the API before the change\nis made to avoid anything getting out of sync.\n  \n\n\nIt seems like a whole lot to think about but that is why design reviews pull in\nmultiple people from various departments or walks of life. Getting all these\naspects nailed down during a design review means you can make sure the API is\nuseful, well-documented, consistent and standards compliant, hopefully not\nriddled with security holes, and generally reduce the chances of problematic API\nchanges getting into production.\n\nWhere do design reviews happen?\n\nThere are countless ways people try to handle this. One of the most complicated\ncame from a famous API thought leader who suggested folks email Word documents\nfull of feedback around with spreadsheets to track them…\n\nA far more common approach is to put the review process through the same Pull\nRequest workflow that developers are already using to review code changes on\nGitHub, GitLab, Azure Devops, etc.\n\nTo know the best way forward it’s helpful to know the history of the pull\nrequest because it hasn’t been around all that long. For decades developers\nwould email around a diff file (short for difference) called new-function.diff\nand people would chat about that technical change on their IRC channels or\naround the water cooler.\n\nIt was a massive faff and multiple diffs could conflict, so when Git gained\npopularity and added the\ngit-request-pull command, that\neventually ended up in GitHub as a Pull Request and the whole code review\nprocess matured.\n\nReviews done right with Pull Requests\nAs the pull request matured it became the home for everything, allowing everyone\ncan see a summary of changes, view source code if they want, see previews,\ncomment on specific lines, suggest changes, and people could checkout the\nchanges if they felt like playing around. All of these abilities gave far more\npeople the option to interact with changes in their own preferred way, to see\nwhat is changing, and if it matters to them.\n\nThis is exactly how code changes should be managed, but API design reviews are\nnot code reviews. The design review is more interested in the interface being\nbuilt by the API than the source code on the inside, and forcing people to read\ncode to work out what that might mean in terms of HTTP and JSON is not helpful.\n\nOnly the developers should be worrying about the code, the system architects,\ngovernance teams, and technical writers want to have a higher level view instead\nof having to learn every single teams’ favourite web application frameworks\nconventions and syntax.\n\nThe role of API specifications\n\nThis is where API descriptions like OpenAPI and AsyncAPI come in. When stored\ntogether with the source code (and used for this like contract testing to ensure\nthe code and description match perfectly) it means these discussions can be had\naround the YAML/JSON that describes the API instead of getting everyone pouring\nover the source code.\n\nA huge step in the right direction, but reviewing hundreds of lines of YAML\nchanges is not easy or fun. Some tooling has appeared trying to move the whole\nAPI design review process into a rough approximation of Git that’s all trapped\nin their walled garden and requires constant syncing. Bump.sh instead focuses on\nallowing design reviews happen in the pull request, and we’ll see how to handle\nthat below.\n\nDiffs Aren’t Design Reviews\n\nTechnical writers often report difficulty staring directly into a pull request\nthat’s making loads of changes to massive OpenAPI documents.\n\nA single pull request might involve anything from reordering some parameters,\ncomplete reorganization of the file structure of infinite $ref‘ed YAML, to a\nbreaking change that could the vast majority of integrations API consumers. It\ncan all be lost in a sea of red and green changes in the “diff” view.\n\nTechnical writers shouldn’t need to learn YAML and stare into the void like\nthis, nor should they need to use complex CLI tools or anything else just to see\nwhat has changed in an API. Nobody should need to do that.\n\nBump.sh helps simplify this process by analyzing OpenAPI documents and\nidentifying meaningful changes so tech writers can focus on relevant updates\nwithout getting lost in unnecessary details.\n\nWhy Not All Changes Matter\n\nSome changes in OpenAPI documents have no impact on API consumers, such as:\n\n\n  Splitting larger documents into smaller pieces with $ref.\n  Renaming referenced files.\n  Renaming component names (e.g. renaming components.schemas.Something which\ndoes not change the actual API)\n  Reformatting YAML for consistency because some documents were an awkward\nmixture of 2 spaces and 4 spaces. 🤢\n\n\nThese modifications might make the OpenAPI documents cleaner, but do not alter\nthe API’s behavior. The huge wall of changes would look like loads of change is\nhappening, and maybe some changes have been sprinkled into what was otherwise\njust a formatting change.\n\nHere’s a change which looks like it’s probably not going to break anything, it’s\njust moving an inline definition of a parameter into a reference, and adding a\nnew optional parameter.\n\n\n\nUnfortunately when copying and pasting some of the reusable parameter\ndefinitions elsewhere something was changed, and that would be hard to spot\nwithout having both definitions open on two different monitors and comparing the\ntwo closely. Thankfully Bump.sh can handle that tedious work for you.\n\n\n\nBump.sh automatically filters out inconsequential YAML/JSON changes and automates all of the following:\n\n\n  See only relevant changes – Filtering out noise from YAML/JSON\nrestructuring and internal updates.\n  Get automatic pull request comments – Highlighting meaningful updates\ndirectly in pull requests.\n  Be alerted to breaking changes – Clearly identifying changes that impact\nAPI consumers.\n  Track API history over time – Keep a clear record of modifications without\nmanually digging into commits.\n\n\nHandling API Design Reviews\n\nOk so we’ve got the theory down, how do you actually do this?\n\n1. Get Bump.sh into Pull Requests\n\nFirst of all lets get Bump.sh running on pull requests. Bump.sh users working\nwith GitHub may already have a .github/workflows/bump.yml workflow, but if not\nadd one. Other continuous integration\nproviders are supported but we’ll stick to GitHub\nActions for this guide.\n\nname: Check &amp; deploy API documentation\n\non:\n  push:\n    branches:\n      - main\n\n  pull_request:\n    branches:\n      - main\n\npermissions:\n  contents: read\n  pull-requests: write\n\njobs:\n  deploy-doc: #optional\n    if: ${{ github.event_name == 'push' }}\n    name: Deploy API documentation on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;BUMP_DOC_ID&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: openapi.yaml\n\n  api-diff:\n    if: ${{ github.event_name == 'pull_request' }}\n    name: Check API diff on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n      - name: Comment pull request with API diff\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;BUMP_DOC_ID&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: openapi.yaml\n          command: diff\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n\n\nThe deploy-docs job is doing what it suggests, deploying API documentation\nwhen merged to main. That’s the standard workflow and can be removed if you\ndon’t need that right now.\n\nThe important part here is the api-diff job. Once you’ve updated\n&lt;BUMP_DOC_ID&gt; to the ID found in the Bump.sh API settings page, popped in the\ntoken hopefully via the Secrets interface on GitHub, and pointed the file:\nopenapi.yaml to where the OpenAPI or AsyncAPI document lives, Bump.sh will\nstart adding summaries of changes to pull requests as comments.\n\n2. Reviewing Changes\n\nWhenever a pull request is made, or changes are pushed to that pull request,\nBump.sh will automatically scan the changes being made to the OpenAPI/AsyncAPI\ndocuments. Any consequential changes will be highlighted in the summary of\nchanges.\n\n\n  New endpoints or properties.\n  New query string parameters added.\n\n\nBreaking changes will be flagged so they’re hard to miss.\n\n\n  Removing or renaming API endpoints.\n  Making an optional property required.\n  Altering response structures.\n\n\nRaising breaking change warnings like this can help system architects and other\nreviewers decide on a level of panic. It could well be that property has been\ndeprecated for a long time and everyone knows no consumers are actually using it\nany more, so Panic Level 0, carry on.\n\nIf the summary flags up something that’s not immediately clear, adds a new\nendpoint that needs more review, etc. then the Preview feature can be used.\n\n3. Preview changes\n\nWhen Bump.sh comments on a pull request with the summary of changes, there is\nalso a little link on there marked Preview and this is pretty magical.\n\n\n\nInstead of just reviewing the YAML changes and hoping the docs look good when\nyou merge, you can go and review the docs to make sure all is good before you\nmerge.\n\nTechnical writers can use this to make sure descriptions are good enough, or if\ndevelopers won’t do it tech writers can make sure their overlays are improving\ndescriptions correctly.\n\nSystem architects can make sure that new endpoints look solid, with appropriate\ndata being accepted and returned to solve the needs for clients.\n\nEveryone can all review the parts of the changes they are interested in, and\ncomment back with concerns, or use GitHub Suggestions to make improvements.\n\n4. Approve Changes\n\nIf everything is fine then reviewers can hit Approve on the pull request. When\nAPI code and API descriptions are in the same repository can be a little\nconfusing as it feels like a technical writer is approving a code change, but\nthis can be solved with Code Owners \nin GitHub.\n\nBy assigning the API descriptions to the appropriate review teams, and the code\nis assigned to the code, you can make sure both relevant groups have approved\nthe changes to stop problems squeaking by.\n\nCreate a new file called CODEOWNERS in .github/ or the root of the\nrepository, and use the following syntax.\n\n# CODEOWNERS\n\ndocs/* @org/review-team\n\nsrc/* @org/dev-team\n\n\nThis says that changes made to docs/openapi.yaml or any of the other\nreferenced bits of description should be approved by the review team, and any\ncode changes will need to be made by the dev team. You can then go into branch\nsettings and make sure that pull reviews are approved by code owners before\nprogressing.\n\nYou can get as creative as you like with this, and have all stakeholders\ninvolved so there system architects and the governance teams all get their say\nbefore things progress.\n\nMake sure its possible to bypass this in emergencies, like pushing a hotfix, but\ngenerally speaking this should help to make sure things always get better and\nthings don’t sneak through.\n\n5. Linting to reduce repetition\n\nThese reviews can end up with a fair amount of repetition, with folks arguing\nover things like which naming conventions to use or whether there should be full\nstops at the end of descriptions or not. Everything is far more efficient when\nrules are written down, and that tedious stuff can be automated away with API\nlinting using tools like\nvacuum so that its already handled before even getting humans involved with the\ndesign review.\n\n\n\nWith Bump.sh spotting changes and offering previews, vacuum highlighting\nconcerns automatically, API design reviews should be a pretty simple affair.\n\nAPI design reviews don’t have to be a struggle for tech writers. With Bump.sh,\nyou can automate the process of detecting relevant changes, stay on top of\nbreaking updates, and manage API documentation efficiently. All of this can be\ndone without needing to deep dive into YAML or complex IDEs.\n\nLet machines do the heavy lifting so you can focus on delivering great\ndocumentation, and steer the evolution of your APIs to make them useful and\nprofitable.\n\nConclusion\n\nAPI design reviews are a critical part of ensuring that APIs are consistent, usable, and aligned with organizational standards. By leveraging tools like Bump.sh, you can streamline the review process, reduce manual effort, and focus on meaningful changes that impact API consumers.\n\nAutomating repetitive tasks, integrating reviews into pull requests, and using API linting tools can help teams collaborate effectively and maintain high-quality APIs. With the right processes and tools in place, API design reviews don’t have to be hard—they can be an opportunity to build better APIs and foster cross-functional collaboration."
        },
        {
          "id": "guides-bump-sh-tutorials-stainless-integration",
          "title": "Integrate Stainless with Bump.sh API docs",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Bump-sh tutorials",
          "tags": "",
          "url": "/guides/bump-sh-tutorials/stainless-integration/",
          "content": "Stainless transforms your OpenAPI specification into powerful, future proof, and ergonomic SDK client libraries that developers\nactually want to use. You can connect these SDKs with your Bump.sh API documentation to create a\nseamless workflow:\n\n\n  SDK Generation: Your OpenAPI spec becomes the source of truth for generating robust and polished client libraries\n  Code Sample Injection: Each API endpoint in your documentation is enhanced with language-specific implementation examples\n  Live Documentation: Developers see exactly how to call your API using your official SDKs alongside the raw HTTP details\n\n\n\n\nThis integration automatically injects idiomatic code samples for each endpoint, bridging the gap between documentation and implementation. Developers get exactly what they need: copy-paste-ready examples that demonstrate your API in action. As your API evolves, both your SDKs and documentation automatically stay in sync.\n\nThis guide will walk you through the setup process to create this seamless developer experience.\n\nPrerequisites\n\nBefore starting, make sure you have:\n\n\n  An account on Stainless\n  An existing Bump.sh documentation project\n  Your OpenAPI specification\n\n\n1. Create Your SDKs in the Studio\n\n\n  Log in to the Stainless dashboard\n  Click “New Project” and upload your OpenAPI specification\n  Stainless will analyze your API and create an initial SDK configuration\n\n\nThe SDK Studio provides an immediate preview of your SDK structure based on your API’s resources, methods, and models.\n\n\n\nWhen first created, Stainless organizes your API into logical resources, but the exact names and hierarchy is up to you,\nallowing you to create the most intuitive experience for your users.\n\nThe Studio lets you customize everything from resource naming to authentication schemes - dive into the configuration\nguide to learn how to prepare SDKs developers will love.\n\n2. Generate Code Examples\n\nIn the Studio, add the following to your Stainless configuration file:\n\nopenapi:\n  code_samples: \"bump.sh\"\n\n\nThis tells Stainless to create a copy of your OpenAPI file extended with SDK code examples, in a format Bump.sh can\nrender. For advanced integration options and support for other documentation platforms, explore the documentation integration guide.\n\n3. Create a Stainless API key\n\nGo to your Stainless org page, select the “API Keys” tab, and create a new key.\n\n\n\nNote: the API key should be considered secret and will only be shown once, be sure to copy and store it to a safe location,\nsuch as a password manager.\n\nYou can now navigate to the GitHub repository where your OpenAPI spec is located, and add a GitHub secret named STAINLESS_API_KEY,\nwith the API key as value.\n\n4. Connect Stainless with Bump.sh\n\nThe most efficient way to keep your SDK in sync with your documentation is through CI/CD automation.\nStainless provides a\nGitHub Action to simplify that process:\n\n# File .github/workflows/ci.yml\n\nname: Upload OpenAPI spec to Stainless and Bump.sh\n\non:\n  push:\n    branches:\n      - main\n\n  pull_request:\n    branches:\n      - main\n\n  workflow_dispatch:\n\npermissions:\n  contents: read\n  pull-requests: write\n\njobs:\n  update-docs:\n    if: ${{ github.event_name == 'push' || github.event_name == 'workflow_dispatch' }}\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Push spec file to Stainless\n        uses: stainless-api/upload-openapi-spec-action@main\n        with:\n          stainless_api_key: ${{ secrets.STAINLESS_API_KEY }}\n          project_name: &lt;your-stainless-project-name&gt;\n          # Note: this should be the path to your specification file\n          input_path: \"path/to/my-company-openapi.json\"\n          # Note: this file will be your spec extended with code samples\n          output_path: \"./openapi-with-code-samples.json\"\n\n      - name: Deploy API docs to Bump.sh\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;your-bump-doc-id&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          # Note: be sure this is pointing to the extended spec file\n          file: \"./openapi-with-code-samples.json\"\n\napi-diff:\n  if: ${{ github.event_name == 'pull_request' }}\n  name: Check API diff with Bump.sh\n  runs-on: ubuntu-latest\n  steps:\n    - name: Checkout\n      uses: actions/checkout@v4\n\n    - name: Comment pull request with API diff\n      uses: bump-sh/github-action@v1\n      with:\n        doc: &lt;your-bump-doc-id&gt;\n        token: ${{secrets.BUMP_TOKEN}}\n        # Note: this should be the path to your specification file\n        file: \"path/to/my-company-openapi.json\"\n        command: diff\n      env:\n        GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n\n\nThis workflow will do two things:\n\n\n  Whenever a Pull Request is created or updated, Bump.sh will generate a diff and submit a PR comment\n  When commits are pushed to your main branch, your OpenAPI spec file is uploaded to Stainless, triggering a new\nupdate to your SDKs. And a copy of your specification file extended with code samples is submitted to Bump.sh, to\nensure your docs are in sync.\n\n\nBy combining Stainless’s SDK generation with Bump.sh’s documentation capabilities, you’re providing developers with a seamless experience from discovery to implementation."
        },
        {
          "id": "guides-openapi-leveraging-the-openapi-specification-for-api-governance",
          "title": "Leveraging the OpenAPI Specification for API Governance",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "OpenAPI",
          "tags": "",
          "url": "/guides/openapi/leveraging-the-openapi-specification-for-api-governance/",
          "content": "API governance is critical for ensuring consistency, security, and quality in enterprise API programs. However, maintaining governance across a distributed organization can be challenging, especially as teams grow and APIs proliferate. In some cases, organizations opt to define guidelines rather than API governance in an attempt to reduce the work required day-to-day.\n\nThe OpenAPI Specification (OAS) provides a structured, machine-readable format for defining APIs. By leveraging OpenAPI, organizations can enhance their API governance efforts, streamline design reviews, automate compliance checks, and create a culture of continuous improvement. In this article, we will explore the differences between API governance and guidelines, the true purpose of API governance, and how OpenAPI can drive better API governance practices.\n\nThe Importance of API Governance\n\nWhile terms like “guidelines” or “guardrails” may sound like a light touch approach to managing APIs, they often result in optional practices rather than consistent ones. When guidelines are implemented, they offer more generalized considerations rather than prescribed rules to which teams will be held accountable. Relying purely on guidelines leads to a lack of cohesion across teams, missed automation opportunities, and even riskier outcomes when policies are loosely interpreted or inconsistently followed.\n\nAPI governance establishes rules and best practices for designing, building, and managing APIs within an organization. Effective governance ensures:\n\n\n  Consistency: APIs follow a unified design language, improving usability.\n  Security: Standardized authentication, authorization, and data handling policies.\n  Scalability: APIs are built with future growth in mind, avoiding breaking changes.\n  Developer Experience: Well-documented, predictable APIs accelerate adoption.\n\n\nTraditional governance models often rely on manual reviews, which can be slow, subjective, and difficult to enforce across multiple teams. OpenAPI helps bridge this gap by providing a machine-readable framework for enforcing governance at scale.\n\nUsing OpenAPI to Drive API Design Reviews\n\nOpenAPI serves as a single source of truth for API definitions. By leveraging OpenAPI during design reviews, organizations can:\n\n  Use OpenAPI documents as descriptions of the API’s purpose, behavior, and schema definitions.\n  Facilitate early discussions between API producers, consumers, and governance teams.\n  Align APIs with enterprise standards before and during the development process.\n  Simplify collaboration by providing a common language for stakeholders.\n\n\nBy making OpenAPI part of the design review process, teams can proactively address inconsistencies and security risks, preventing costly rework later in the API lifecycle.\n\nAutomating Checks with a Linter Workflow\nManual reviews can be time-consuming and prone to human error. Automating API governance using a linter workflow helps enforce standards at scale.\n\nAPI linting is an automated process that checks an OpenAPI document against predefined rules, typically defined by an API style guide. These rules can enforce:\n\n  Naming conventions (e.g., using camelCase or snake_case).\n  Security requirements (e.g., enforcing authentication).\n  Proper documentation (e.g., ensuring every endpoint has descriptions).\n  Consistent response codes and error handling.\n\n\nThe use of a linter results in several benefits:\n\n\n  Early Error Detection: Mistakes are caught before APIs reach production.\n  Enforced Standards: Ensures APIs remain consistent across teams.\n  Reduced Review Effort: Automates repetitive checks, freeing up governance teams for strategic tasks.\n\n\nBy implementing linting in a CI/CD pipeline, organizations can enforce API governance continuously, reducing the risk of poor design decisions slipping through.\n\nTools for OpenAPI Linting\n\nSeveral tools support OpenAPI linting, including:\n\n\n  Spectral – A powerful linter that can validate OpenAPI documents against custom rules.\n  vacuum - A drop-in replacement for Spectral that focuses on high performance linting while leveraging investments in existing Spectral rule sets\n  OpenAPI Validator – Ensures OpenAPI files are correctly formatted and adhere to best practices.\n\n\nThe Power of API Design Reviews\n\nOne of the most effective ways to improve API design is to collaborate on the design early and often. OpenAPI makes these discussions more productive by providing a tangible, structured representation of the API. By shifting feedback on API design to earlier in the delivery process, organizations can experience several benefits:\n\n\n  Prevents poor design choices from being implemented by catching them early, when the cost of change is much lower.\n  Aligns stakeholders (developers, architects, security teams, product managers) before coding begins to ensure that the right thing is being built.\n  Encourages feedback from API consumers to ensure usability and alignment with their needs.\n  Reduces technical debt and rework by resolving issues at the design stage.\n\n\nBest Practices for API Discussions\n\n\n  Design-First Approach: Define the OpenAPI spec before writing any code.\n  Collaborative Reviews: Involve multiple stakeholders to get diverse perspectives.\n  Use Mock Servers: Tools like Prism and Microcks.io can generate mock responses from an OpenAPI document, allowing teams to test API behavior before implementation.\n  Version Control OpenAPI Documents: Treat API specifications like code, with version control and change tracking.\n\n\nBy fostering discussions early in the API lifecycle, teams can prevent inconsistencies and ensure APIs align with organizational goals.\n\nLeveraging Design Reviews for Coaching and Improvement\n\nAPI design reviews shouldn’t just be about enforcement—they should also be opportunities for education and growth. Governance teams can use OpenAPI-based design reviews to coach developers on best practices. This offers several advantages for organizational improvement, including:\n\n\n  Provide Constructive Feedback: Explain the reasoning behind required changes instead of just enforcing rules.\n  Use OpenAPI as a Teaching Tool: Show examples of well-designed APIs and how they adhere to standards.\n  Encourage a Business-Focused API Design: Help teams understand the impact of API decisions on consumers and the business value produced.\n\n\nBy embedding coaching into API governance, organizations can cultivate a culture of continuous improvement and high-quality API design.\n\nConclusion\n\nOpenAPI is a powerful tool for strengthening API governance by providing a structured format for defining APIs, enabling automated checks through linting workflows, facilitating productive design discussions, and turning governance into a coaching opportunity. By integrating OpenAPI into the API development lifecycle, organizations can enforce standards efficiently, reduce rework, and improve API quality. Combining automation with human collaboration ensures that governance is both scalable and effective, leading to better APIs and a more mature API program."
        },
        {
          "id": "guides-bump-sh-tutorials-api-discovery-using-bump-sh-hubs",
          "title": "Unlocking API Discovery with Bump.sh Hubs in Your Platform Engineering Strategy",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Bump-sh tutorials",
          "tags": "",
          "url": "/guides/bump-sh-tutorials/api-discovery-using-bump-sh-hubs/",
          "content": "APIs are at the core of modern enterprise architectures, powering integrations, automation, and digital products. Yet, API sprawl remains a persistent challenge—APIs are often scattered across different teams, tools, and repositories, making discovery and governance difficult.\n\nThis is where Bump.sh Hubs come in. By serving as a unified API catalog, Bump.sh helps enterprises streamline API discovery, governance, and collaboration across teams. Whether you’re managing internal APIs, partner APIs, or external APIs, Hubs provide a single source of truth, accelerating developer onboarding and ensuring consistency across REST and AsyncAPI specifications.\n\nBump.sh Hubs: A Foundation for API Discovery\n\nFor platform engineering teams, API discovery is a key enabler of developer productivity and efficiency. If APIs aren’t easily discoverable, teams waste time duplicating work, re-learning existing integrations, or struggling to keep up with API changes.\n\nBump.sh Hubs address these challenges by:\n\n✅ Providing a centralized API catalog for internal teams and partners\n✅ Unifying API types (REST, AsyncAPI, GraphQL, etc.) in one platform\n✅ Offering real-time changelogs, ensuring developers stay up-to-date with API changes\n✅ Enhancing API searchability, both internally and externally\n\nThis makes Hubs a crucial piece of a platform engineering strategy, enabling seamless API consumption across different teams and use cases.\n\nHow Organizations Use Bump.sh Hubs\n\n1. Internal API Discovery: Breaking Down Silos\n\nLarge organizations often struggle with API silos, where teams build APIs independently without a shared catalog. This leads to inefficiencies, redundant work, and difficulty enforcing governance.\n\nBy adopting Bump.sh Hubs, organizations can:\n\n\n  Centralize all internal APIs in one place, making them easily discoverable\n  Standardize API documentation, ensuring a consistent developer experience\n  Track API updates via changelogs, allowing developers to quickly adapt to new versions\n\n\n2. Partner-Facing API Catalogs\n\nFor enterprises with partner ecosystems, making APIs easily accessible and well-documented is critical. Bump.sh Hubs act as an API storefront, allowing partners to:\n\n\n  Browse and explore available APIs\n  Understand API updates through changelogs\n  Access API specifications in a structured, user-friendly format\n\n\nThis improves API adoption and reduces integration friction, helping enterprises scale their API-driven partnerships.\n\n3. Enabling API Discovery with SEO &amp; Search Indexing\n\nBeyond internal and partner-facing use cases, Bump.sh Hubs can improve external API discoverability through search engine optimization (SEO).\n\nOne key example is Elastic, which integrates Bump.sh into their API documentation strategy. Elastic uses Bump.sh-generated sitemaps to feed their website’s search engine, allowing developers to search for any API operation directly from their main documentation site.\n\nTry it yourself by searching “get agent status summary” in the Elastic docs search, and you’ll see that Bump.sh-powered API operations appear directly in the results.\n\nThis means:\n🔹 API endpoints are searchable both internally and externally\n🔹 Developers can quickly locate API operations without digging through docs\n🔹 Enterprises can improve API discoverability through SEO optimization\n\nBump.sh also supports API.json, making it easy to generate and expose machine-readable API metadata that can be indexed for improved searchability.\n\nIntegrating Bump.sh Hubs into Your Platform Engineering Strategy\n\nBump.sh Hubs aren’t just another documentation tool—they play a key role in modern platform engineering efforts by enabling self-service API discovery, governance, and version management.\n\n🔹 Connect with CI/CD pipelines to automatically update API catalogs\n🔹 Use webhooks &amp; notifications to alert teams to breaking changes\n🔹 Enforce API governance by making standards &amp; guidelines easily accessible\n🔹 Enhance API observability by tracking version changes &amp; dependencies\n\nBy integrating Bump.sh into your API governance framework, you can reduce API fragmentation, accelerate development, and improve API adoption across your enterprise.\n\nFinal Thoughts\n\nAPIs are more valuable when they are easily discoverable, well-documented, and properly governed. Bump.sh Hubs provide enterprises with a scalable way to centralize, organize, and manage APIs, aligning with today’s platform engineering best practices.\n\nIf your organization is struggling with API sprawl, inconsistent documentation, or poor API discoverability, it’s time to explore how Bump.sh Hubs can unify your API ecosystem.\n\n🚀 Want to see it in action? Get started with Bump.sh and transform how your teams discover, manage, and collaborate on APIs."
        },
        {
          "id": "guides-api-basics-using-smithy-to-define-your-apis",
          "title": "Using Smithy to Define Your APIs",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "API Basics",
          "tags": "",
          "url": "/guides/api-basics/using-smithy-to-define-your-apis/",
          "content": "The OpenAPI Specification is the de facto format for capturing REST-based API details. However, there have been some recent alternatives that have emerged to address different styles of workflows. One such alternative is Smithy, an interface definition language that supports modeling, designing, and generating API documentation, client code, and server code. In this article, we will look at Smithy’s features and how it might support your organization’s preferred API-first approach to API design and delivery.\n\nWhat is Smithy?\n\nSmithy is an interface definition language (IDL) and tooling framework for defining service APIs and data structures. Initially developed by AWS, it is used to describe APIs in a way that is both machine-readable and human-friendly. Smithy allows for strong typing, model validation, and code generation, making it a powerful tool for defining REST and other service-based APIs.\n\nSmithy Features\n\nSmithy can be used to define REST APIs by modeling resources, operations, and data types in a structured way. This helps standardize API development, generate client/server code, and enforce best practices. Key features of Smithy include:\n\n  Service Definition – Smithy defines services with their operations, input/output structures, and error handling.\n  Resource Modeling – Supports RESTful resource-based modeling.\n  Data Modeling – Uses strong typing and constraints for API inputs/outputs.\n  Validation – Ensures API definitions conform to best practices while avoiding common pitfalls.\n  Code Generation – Can generate SDKs, OpenAPI specs, and server stubs using either built-in or third-party generators.\n  Extensibility – Can be customized with plugins and integrations. I would recommend starting with the Awesome Smithy GitHub project as a starting point for discovering plugins.\n\n\nSmithy’s Role in the API-First Lifecycle\n\nSmithy fits into multiple stages of the API-first lifecycle:\n\n\n  \n    \n      Lifecycle Stage\n      Smithy’s Role\n    \n  \n  \n    \n      Design &amp; Planning\n      Provides a structured way to define APIs before coding.\n    \n    \n      API Standardization\n      Enforces consistency across multiple teams and services.\n    \n    \n      Code Generation\n      Generates OpenAPI specs, client SDKs, and server stubs.\n    \n    \n      Validation &amp; Governance\n      Ensures API definitions conform to enterprise standards.\n    \n    \n      Documentation\n      Provides clear and machine-readable API documentation.\n    \n    \n      Evolution &amp; Versioning\n      Supports API versioning while ensuring backward compatibility.\n    \n  \n\n\nSmithy is ideal for API-first organizations. API designers first define the operations and models using the Smithy format, then generate an OpenAPI document as well as client and server code.\n\nSmithy vs. OpenAPI\n\nOpenAPI is the gold standard for documenting APIs. So, why do we need another format? While OpenAPI is great at documenting an existing REST-based API, it lacks support for the full API-first lifecycle. For OpenAPI documents to be created, the bulk of the API design must be completed. This is due to OpenAPI’s focus on paths and HTTP methods as the first two elements of a documented API operation.\n\nSmithy is more flexible and extensible, allowing better data modeling and validation. Models can be defined prior to the operations and validated as part of a continuous delivery pipeline. As the API definition is expanded with operations and error responses, traits may be applied to help shape how the API will be implemented, including any HTTP-specific details. For organizations that leverage OpenAPI-related tooling as part of their delivery pipeline, Smithy can generate OpenAPI specs, bridging the gap between the two quite easily.\n\nThe Smithy Ecosystem\n\nThe Smithy ecosystem is comprised of the following components:\n\n\n  The Smithy Specification - a formal specification for the IDL, types, and bindings\n  The Smithy Style Guide - recommended style guide for writing Smithy-based IDL files in a consistent manner, whether you are a team of one or one of many teams within your organization\n  The Smithy CLI - a command-line interface that helps you build your models, run ad-hoc validation, compare models for differences, query models, and more\n  Smithy Gradle Plugins - integrates Smithy with the Gradle build system, making it possible to work locally or within a delivery pipeline to validate your models and generate artifacts.\n  The Smithy Code Generator Guide - a guide to building your own code generators using Smithy, including details on how the Smithy generator’s lifecycle and plugin architecture work.\n  Smithy Examples - a GitHub repository containing examples to help you get started with Smithy.\n\n\nAs you may have noticed, this is more than a few random tools and examples. Smithy is well documented and thoughtfully designed to be customized in a variety of ways.\n\nHow Smithy Works\n\nAt a high level, Smithy follows a five-step workflow. Let’s look briefly at each of the steps in the workflow and how it contributes to a powerful and flexible API lifecycle. Along the way, I’ll link to files from their Smithy CLI example repository to avoid cluttering this walkthrough.\n\nStep 1. Define a Smithy Build File\n\nThe first step is to write a smithy build file. This file uses a JSON format and defines working directories, plugins, and other details that your project will need to use Smithy successfully. You can look at an example here. \nYou will probably spend some time in this file initially, but once you have everything working the way you want, you will rarely need to revisit this file.\n\nStep 2. Define API Models\nThe next step is to write a .smithy model file to define services, resources, and operations. This can be done iteratively, starting with some basic details and expanding over time. This is different from a traditional OpenAPI workflow, where we must know most or all of our API design details before we can produce a valid OpenAPI document.\n\nSmithy models are written in a human-readable format called Smithy IDL. This format helps to streamline authoring and reading models. We can see an example weather.smithy model file offered in their quickstart. The model file is placed into the models directory of the project. This folder was referenced in the build file from step 1, helping the Smithy CLI tool to locate all models and process them each in turn. \nYou will define one or more model files within your project. Smithy supports namespacing conventions, allowing you to easily reuse and reference different models as needed.\n\nStep 3. Validate Models\n\nNext, we need to ensure our models follow best practices and governance rules. Using the Smithy CLI tool within our project, we can run smithy validate to produce a report of any invalid declarations. Common validation checks include:\n\n\n  Ensure required fields are properly defined.\n  Ensure operations have valid HTTP mappings.\n  Enforce naming conventions (e.g., camelCase for fields).\n\n\nWe can request that only a specific model is valid or we can validate all models within our project at once.\n\nStep 4. Configure Projections\n\nAs our models start to take shape, we can add different projections into our build file. Projections are custom transformations of the model into different output formats. There are a few different types of projections available:\n\n\n  \n    \n      Projection Type\n      Description\n    \n  \n  \n    \n      OpenAPI\n      Converts Smithy to an OpenAPI (Swagger) spec.\n    \n    \n      SDKs\n      Generates client SDKs for multiple languages.\n    \n    \n      Server Code\n      Creates server stubs for API implementations.\n    \n    \n      JSON AST\n      Converts Smithy into machine-readable JSON for further analysis, linting, or other uses.\n    \n  \n\n\nGiven Smithy’s pluggable design, you can add different types of projections available within Smithy, projections from third-party providers, or create your own custom projections. \nOnce you have defined the projection(s) you wish to enable for your project, the final step is to generate outputs.\n\nStep 5. Generate Outputs\nThe final step in the workflow is to convert the Smithy model into OpenAPI specifications, SDKs, server stubs, or other artifacts. Smithy recommends integrating this workflow into your CI/CD pipeline, publishing any generated documentation artifacts, such as OpenAPI documents, into your Bump.sh developer portal for discovery.\n\nFinal Thoughts on Smithy\nSmithy is a very powerful tool for managing your API first lifecycle. Unlike OpenAPI documents, there is more work to do initially to get everything set up and configured properly. However, there is considerable flexibility in being able to define your API resources and operations in Smithy IDL format, run validators to ensure compliance against your organization’s style guide conventions, and expand the details over time until you have a complete API design that can be used to generate client libraries, server-side stubs, and OpenAPI documentation."
        },
        {
          "id": "guides-api-basics-docs-as-code-elevating-api-documentation-with-developer-workflows",
          "title": "Elevating API Documentation with Developer Workflows Using Docs as Code",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "API Basics",
          "tags": "",
          "url": "/guides/api-basics/docs-as-code-elevating-api-documentation-with-developer-workflows/",
          "content": "Docs as code is a powerful methodology that aligns documentation processes with software development workflows.  It treats documentation as a first-class citizen in the development lifecycle by applying a similar approach to the standard development process. Let’s explore what it is, how it works, and how you can get started.\n\nWhat is Docs as Code?\n\nDocs as Code combines software development practices—such as version control, automated testing, and continuous integration— to your documentation processes. Technical writers and developers are able to collaborate more effectively as a result, ensuring that API documentation remains up-to-date, consistent, and of high quality. For large organizations with multiple teams and shared technical writers, docs as code offers a structured framework to blend developer-generated documentation and improvements and additional documentation produced by technical writers.\n\nHow Docs as Code Transforms Technical Writing\n\nApplying continuous delivery principles to documentation brings numerous advantages, such as improved collaboration, automation, and version control. Most traditional methods of technical writing include a few elements of traditional software delivery. Docs as code combines version control, automated testing of documentation, and continuous deployment into an optimized workflow.\n\nTrack Changes with Version Control\n\nVersion control helps to track changes to documentation over time, allowing mistakes to be reverted or new revisions to be built and released using a single snapshot. Version control systems, such as Git, are often used by developers and therefore can be leveraged for docs as code.\n\nBy managing documentation in a repository alongside the codebase, teams can:\n\n\n  Maintain a historical record of changes\n  Revert to previous versions when necessary\n  Enable concurrent editing and review processes\n  Facilitate branching and merging to manage documentation updates\n\n\nAre you new to git? Check out our article that provides an introduction to git concepts.\n\nAutomated Testing to Enforce Quality\n\nAutomated testing enforces documentation quality, similar to automated software test suites. A variety of tools are often used to address the various aspects of documentation quality, including:\n\n\n  Linting tools (e.g., Vale, Markdownlint) to enforce style guides and formatting consistency\n  Link checkers to validate internal and external links\n  Schema validation tools to ensure API documentation aligns with actual API definitions\n  Spell checkers and grammar checkers to catch errors before publication\n  OpenAPI linters (e.g., vacuum) to enforce compliance with your organization’s API style guide.\n\n\nBy automating quality control, documentation teams catch issues early in the writing process and reduce manual proofreading efforts.\n\nPreview Updates with Continuous Integration\n\nContinuous integration (CI) automatically builds and validates content whenever changes are committed. Popular CI/CD tools like GitHub Actions, GitLab CI, and Jenkins can be configured to:\n\n\n  Run tests and linters on new commits\n  Generate preview builds for review before merging\n  Notify stakeholders of issues through automated reports\n\n\nThis ensures that documentation updates go through the same rigorous review process as code, enhancing reliability and consistency.\n\nCheck out our reference on using GitHub Actions with Bump.sh to keep your docs in sync at all times.\n\nContinuous Deployment to Publish Changes\n\nContinuous deployment (CD) enables teams to publish documentation automatically when changes are merged. By integrating documentation pipelines with an API documentation platform (e.g., Bump.sh), organizations can:\n\n\n  Deliver real-time updates to developers\n  Reduce the lag between API changes and documentation updates\n  Ensure that published content is always in sync with the latest version of the API\n\n\nAutomating deployment removes bottlenecks in the publishing process and empowers developers to access up-to-date documentation instantly.\n\nLeveraging Docs as Code For Faster, More Efficient Workflows\n\nImplementing docs as code practices has a direct impact on documentation quality, benefiting both technical writers and developers. By treating documentation as code, organizations can ensure that API documentation is version-controlled and aligned with actual API changes. Automated validation tools help enforce consistency, making documentation more reliable and reducing discrepancies.\n\nIn addition, docs as code fosters a collaborative workflow where both developers and technical writers contribute to documentation in a shared repository. By leveraging pull requests, code reviews, and comments, teams can:\n\n\n  Provide feedback in a structured manner\n  Enforce quality standards through automated checks\n  Ensure transparency in documentation changes\n\n\nLeveraging OpenAPI overlays for your API reference documentation helps technical writers to enrich default documentation, without disrupting developer flow. This collaborative approach bridges the gap between developers and technical writers, leading to more comprehensive and developer-friendly documentation.\n\nWith automated testing, continuous integration, and streamlined review processes, documentation updates can be implemented and deployed faster. This agility is crucial in API development, where rapid iterations and frequent releases require documentation to keep pace with evolving features.\n\nFinally, large organizations managing multiple APIs can benefit from the scalability of docs as code. Standardized workflows, automation, and modular documentation structures enable teams to:\n\n  Reuse content across different API versions\n  Maintain consistent formatting and style across documentation sets\n  Reduce the overhead of manual updates and reviews\n\n\nAs the API landscape grows, a docs as code approach ensures that documentation remains manageable, maintainable, and scalable.\n\nPractical Tips and Best Practices\n\nAnne Gentle has been a prominent advocate of the docs as code movement. Her book, Docs Like Code, outlines the principles and practices that organizations can adopt to integrate documentation into their software development workflows. She offers some terrific tips and best practices for applying docs as code into an approachable, repeatable process:\n\n\n  Define a documentation style guide and enforce it with linting tools.\n  Create templates for consistent documentation structure.\n  Implement automated link checking to prevent broken links.\n  Establish a review workflow with pull requests and approvals.\n  Encourage developer participation through documentation sprints and contributions.\n\n\nBy following these best practices, organizations can create a sustainable docs as code ecosystem that supports high-quality documentation at scale.\n\nConclusion\n\nThe docs as code approach transforms API documentation by aligning it with modern software development practices. By leveraging version control, automation, continuous integration, and deployment, organizations can:\n\n\n  Enhance documentation accuracy and consistency\n  Improve collaboration between developers and technical writers\n  Accelerate documentation updates to match API changes\n  Simplify maintenance and scalability across multiple teams\n\n\nFor technical writers and developers responsible for API documentation, adopting a Docs as Code workflow is not just an option—it’s a necessity for keeping up with the rapid pace of API development. By embracing these principles, organizations can ensure that their documentation remains an integral and valuable part of the API ecosystem."
        },
        {
          "id": "guides-bump-sh-tutorials-api-linting-with-vacuum",
          "title": "API Linting with vacuum",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Bump-sh tutorials",
          "tags": "",
          "url": "/guides/bump-sh-tutorials/api-linting-with-vacuum/",
          "content": "Whether just starting to build an API and looking for feedback, or improving auditing an old API to see if it’s secure, consistent, and up to scratch, the approach is likely the same: API linting.\n\nThe idea of API linting is much like linting source code or running a spelling/grammar checker on documentation: let’s set some rules, and see if the JSON/YAML is actually following them.\n\n\n  Is the YAML/JSON valid.\n  Is the OpenAPI or AsyncAPI valid against their respective specifications.\n  Is the API following industry standards and best practices.\n  Is the API following relevant security practices.\n  If an organization has an API Style Guide, is it following that?\n\n\nChecking all of this manually once is hard enough, but keeping everything in mind after every single change made by anyone becomes impossible, especially if there are hundreds of developers working on different pieces of the API ecosystem all the time.\n\nFollowing the API design-first workflow can save a lot of time and money by using API linting on brand new APIs as they’re being created, and make sure they’re completely up to scratch before coding begins.\n\nBrief history of API linting\n\nFor decades the highest standard of API quality control was writing up an “API Style Guide”, which was usually a Wiki, PDF, or Word document, which listed all the things people thought meant an API good. What naming convention to use, what authentication methods were preferred, how to structure data in requests and responses, etc. These documents would become huge. Sometimes they were read by API developers, sometimes not, but even when they were it was impossible to remember everything in there, and sometimes it would change, so people were acting off of old memories of outdated interpretations.\n\nThis was obviously not ideal, so folks set out to automate the process, and over the last decade they succeeded in getting API linting and “Automated API Style Guides” from a concept to a best practice. Tools like Speccy got the ball rolling, Spectral drastically expanded the functionality people expected from an API linter, and vacuum took the Spectral format, creating something even more powerful and considerably more efficient.\n\nWhat is vacuum?\n\nvacuum is completely free and open-source, built in Go and engineered from the ground up to be performant, avoiding some of the tricky legacy decisions that lead to Spectral not exactly being a race horse. Whilst there are other linters around, vacuum has the best of both worlds, it’s very quick, and it supports Spectral “rulesets”, so giving the best portability for rulesets being created or reused from elsewhere.\n\nUsing vacuum to improve Bump.sh API documentation\n\nLet’s set up a workflow where vacuum can give us feedback on an API in pull requests. The goal is to make sure that any changes being made to the API are valid, correct, and useful, regardless of who the contributor is, or how much experience they have.\n\nStep 1: Install vacuum CLI\n\nYou can install vacuum as a command-line tool using Brew, NPM, Curl, or Docker, so there’s something for everyone.\n\nThis guide will install vacuum with NPM, and pop it into the Train Travel API so we can see how it all works live. Using NPM will also allow us to run it in GitHub Actions easier later on. Feel free to clone the repository and follow along.\n\ncd ~/src/train-travel-api\n\nnpm install @quobix/vacuum --include=dev\n\n\nWhen installing vacuum like this, run npx vacuum --help confirm it’s installed correctly. For other methods run vacuum --help.\n\nStep 2: Running vacuum locally\n\nLet’s try running vacuum to see how the API does before we worry about any further integrations.\n\nRun vacuum on any OpenAPI available, or download the Train Travel API to use that OpenAPI.\n\nnpx vacuum lint -d openapi.yaml\n\n\nThe output can get quite large, as vacuum comes with a lot of useful rules baked in (so that every user doesn’t have to invent them all by themselves every time.)\n\n\n\nStep 3: Configure rules/rulesets\n\nIf there is anything fundamentally broken with the API description then vacuum will consider that an “Error”, but in this instance it looks like the Train Travel API is missing quite a few examples. Maybe that’s not something I’m too worried about if I’m not using this OpenAPI document for API documentation or mock servers, and if that’s the case for you you can turn off that rule.\n\nTo configure vacuum, create a .vacuum.conf.yaml file in your working directory (ideally the root of a Git repository).\n\n# .vacuum.conf.yaml\nruleset: ./.vacuum/ruleset.yaml\n\n\nNow let’s go make that ./.vacuum directory file and pop a new file in it.\n\n# .vacuum/ruleset.yaml\nextends: \n  - spectral:oas\n\nrules:\n  oas3-missing-example: off\n\n\nBy default we’re using the Spectral “OAS” (OpenAPI Specification) ruleset, which is where all these rules are defined. Creating this config file and new ruleset is overriding the default functionality, but doing something very similar. We’re extending the ruleset so we have all the rules, then turning off only the oas3-missing-example rule.\n\nRunning the lint command again should be a little cleaner.\n\n\n\nIf you spot an error in here you would like to fix, you can use the Location to find out exactly where it is. openapi.yaml:1049:5 means it’s in openapi.yaml on line 1049 and it’s taken a stab at guessing which character of the problem, which in this case is 4 spaces of indent plus where is the thing in the 5th character… no worries, close enough.\n\n\n\nThe problem here was a lack of a description, so pop something in like description: The link to the booking resource. and run it again to see if that fixed the error.\n\nOnce everything is fixed or ignored, you should get a perfect run.\n\n\n\nStep 4: Keep that perfect score\n\nOnce everything is fixed, wouldn’t it be nice to keep it that way?\n\nAdding a new GitHub Action can help us do this, and we don’t need to get things mixed up with existing GitHub Actions you may have for deploying Bump.sh API documentation, updating mock servers, or generating SDKs, it can just be its own new lint.yml workflow.\n\n# .github/workflows/lint.yml\nname: Lint\n\non:\n  pull_request: {}\n  push:\n    branches: [main]\n    paths-ignore:\n      - 'README.md'\n      - 'src/**'\n\njobs:\n  build:\n    name: API Linting\n    runs-on: ubuntu-latest\n\n    permissions:\n      statuses: write\n      checks: write\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v4\n\n      - name: Install dependencies\n        run: npm ci\n\n      - name: Lint API\n        run: npm exec vacuum report -- --junit openapi.yaml lint-results\n\n      - name: Publish Lint Results\n        if: success() || failure()\n        uses: mikepenz/action-junit-report@v5\n        with:\n          check_name: API Lint Results\n          report_paths: lint-results-*.xml\n\n\nThe main work is happening here in the “Lint API” and “Publish Lint Results” stages.\n\nInstead of using npx vacuum lint this workflow uses the npx vacuum report command, which is intended to generate a report of whats passing and whats failing. To create the report in a standard that can be understood by tooling, you can create it in JUnit with --junit, and then we give it the lint-results prefix to distinguish from any other test suite results that may be created.\n\nFinally, the “Publish Lint Results” step reads those results using the JUnit Report GitHub Action. As pull requests are made to the API, warnings and errors will start showing up, giving people the chance to fix the mistakes. These can be set up as “Checks” to block a pull request if there are errors, annotations to show other problems including warnings, or both, depending on various permissions configurations.\n\n\n\nBlocking the build on OpenAPI errors stops contributors from accidentally breaking the OpenAPI document, which will cause all sorts of problems, not least the lack of updates for Bump.sh API Documentation!\n\nSee how to configure the JUnit report action to suit any setup.\n\nSummary\n\nTeams are more efficient when rules are written down. Vacuum speeds up API design reviews, acts as one very small but helpful step towards an API governance program, and can make sure errors are not being introduced to your Bump.sh API documentation, or even the API itself, and when leveraging rulesets like the OWASP ruleset it can even help avoid costly security mistakes.\n\nSetup vacuum for your Bump.sh API today, and see what score it gets."
        },
        {
          "id": "guides-bump-sh-tutorials-generate-sdks-with-speakeasy",
          "title": "Generate SDKs with Speakeasy",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Bump-sh tutorials",
          "tags": "",
          "url": "/guides/bump-sh-tutorials/generate-sdks-with-speakeasy/",
          "content": "The quicker a customer can integrate with your API, the quicker your business will be making money or solving problems. Some users will be happy to integrate directly with the API, but many prefer the ease of working within the programming language through Software Development Kits (SDKs).\n\nThese can be a lot of work to build and keep up to date, but if you’ve got OpenAPI you really don’t need to do all that manually. Generate them automatically with Speakeasy, and publish the code samples to your Bump.sh API documentation!\n\nWhat is Speakeasy\n\nSpeakeasy is Software-as-a-Service similar to Bump.sh, but instead of focusing on documentation they focus on SDK generation. In the past you’d have to use cumbersome Java-based open-source tooling and generally develop your own templates, but with Speakeasy you can simply upload an OpenAPI description document and get type-safe SDKs that your team will be proud of. These SDKs include OAuth 2, retries, pagination, custom code, and more.\n\nStep 1: Set up Speakeasy\n\nHead over the Speakeasy Quickstart or if you’d like to go a little slower you can go through the Speakeasy Introduction. The main thing is to start with installing the Speakeasy CLI which you can do with popular package managers like Homebrew and Chocolatey.\n\nStep 2: Create Your First SDK\n\nOnce you’ve got Speakeasy CLI set up, run the speakeasy quickstart command. You’ll be prompted to generate a workspace on their dashboard if you haven’t already.\n\nThe CLI quickstart wizard will walk you through the process of creating the first SDK, by asking you to point to your OpenAPI document and pick the first language.\n\nFor the sake of the tutorial we’re going with TypeScript, but you can pick from Python, Go, Java, PHP, C#, Ruby, Swift, and more.\n\n\n\nGenerally, you’ll have a “central” repository which contains your OpenAPI, and Speakeasy suggests creating a new repository for each SDK you generate (although monorepo setups are possible). This repository will hold all the code and the relevant configuration to keep it all running, separate from your main OpenAPI and source code.\n\n\n\nFor example, if you ran the speakeasy quickstart from /Users/phil/src/train-travel-api, you might make a new repository for the TypeScript SDK next to that directory in /Users/phil/src/train-travel-sdk-ts.\n\nEnter a path like that into the quickstart, and the local Git repository will be created for you. You can add, commit, and push everything to a new GitHub repository of your choosing.\n\ncd ~/src/train-travel-sdk-ts\ngit remote add origin git@github.com:bump-sh-examples/train-travel-sdk-ts.git\ngit add --all\ngit commit -m \"initial commit\"\ngit push origin main\n\n\nOnce you’ve got everything inside this lovely new generated codebase pushed to GitHub, you can create GitHub Actions to automatically create pull requests when changes are detected on the OpenAPI.\n\nStep 3: Configure GitHub\n\nGitHub Actions help us to automate generating and releasing new versions of the SDKs over time as your API and the OpenAPI that describes it evolve.\n\nspeakeasy configure github\n\n\nThis create a new .github/workflows/sdk_generation.yaml which will do a few things. It’s main job is to set up a cron job which will open pull requests to generate a new SDK for you once a day if there are changes.  To make it work check the output of that command which outlines a bunch of steps, from creating an SPEAKEASY_API_KEY environment variables, and allowing the GitHub Action to open up pull requests.\n\nOne other thing, to make this work on GitHub instead of just working locally, is to update the “target” in the Speakeasy SDK workflow document: .speakeasy/workflow.yaml.\n\nBy default it was set to the following:\n\nsources:\n  Train Travel API:\n    inputs:\n      - location: ../train-travel-api/openapi.yaml\n\n\nThis was causing some confusion for the Speakeasy GitHub action because it was literally looking for that local path when the GitHub Action was being run, and that’s the folder structure on the local machine which isn’t there. To make this work on GitHub you can grab the public “raw” URL:\n\nsources:\n  Train Travel API:\n    inputs:\n      - location: https://raw.githubusercontent.com/bump-sh-examples/train-travel-api/refs/heads/main/openapi.yaml\n\n\nPublic URLs like this will only work for public GitHub repos, but if you cannot make the repo public you can pop this onto S3 or somewhere else.\n\nPlease do all of these steps or things will not work.\n\nCommit and push all of these changes to main, and the repository is in good shape.\n\nStep 4: Publish Code to Package Managers\n\nOnce GitHub is automatically creating pull requests to update the codebase in your repository, you might want to do something with that code, like publish that code as a package to package managers like NPM, PyPI, Packagist, NuGet, and Maven.\n\nFor the sake of the tutorial, we’re publishing to NPM. The easiest way is to follow the standard process of creating a NPM package, by doing it manually at first, then letting Speakeasy take over after.\n\nnpm login\nnpm publish\n\n\nIf this all worked then count to 100 and you should see your new package!\n\n\n\nNow our train-travel-sdk actually exists online, and you can see how it’s going from here.\n\nTo save somebody having to run a command to publish this SDK (and every other SDK you end up making) every time anything changes, we can ask GitHub and Speakeasy to handle all of that for us.\n\nspeakeasy configure publishing\n\n\nThis will create another github workflow which will help publish the package automatically, and should look a bit like this:\n\n# .github/workflows/sdks.yml\nname: Publish SDKs\npermissions:\n  checks: write\n  contents: write\n  pull-requests: write\n  statuses: write\n  id-token: write\n\"on\":\n  push:\n    branches:\n      - main\n    paths:\n      - .speakeasy/gen.lock\n  workflow_dispatch: {}\njobs:\n  publish:\n    uses: speakeasy-api/sdk-generation-action/.github/workflows/sdk-publish.yaml@v15\n    with:\n      target: train-travel-sdk\n    secrets:\n      github_access_token: ${{ secrets.GITHUB_TOKEN }}\n      npm_token: ${{ secrets.NPM_TOKEN }}\n      speakeasy_api_key: ${{ secrets.SPEAKEASY_API_KEY }}\n\n\nNotice it is still using the SPEAKEASY_API_KEY that we used in the generate workflow, but now there’s a NPM_TOKEN we’ll need to add. Grab that from your NPM account under Access Tokens, make sure it has write permission for packages, and save it as NPM_TOKEN in your repo GitHub repository Actions secrets.\n\nNow if you push commits your main branch or merge a pull request, this GitHub Action will automatically help publish a new version of the SDK.\n\n\n\nFrom here, if you change anything relevant in the SDK repo, it’ll publish a new SDK, and if the central OpenAPI changes it will also publish a new SDK. This solves a lot of the frustrations with managing packages too, such as manually bumping version numbers and figuring out if it should be major, minor, or patch, or setting up confusing conventions for automatically doing it like Semantic Release. Speakeasy and GitHub Actions (or similar) can just do all that for you as changes are merged.\n\nWith everything be entirely automated we can leave the SDK to itself, and the DevRel team can focus on tell everyone about how amazing it is.\n\nStep 5: Integrating Code Samples into OpenAPI with Overlays\n\nIn the past people would either have completely separate “API Docs” and “SDK Docs”, or they would spend countless hours copying and pasting SDK examples into the API Docs.\n\nNo more!\n\nYou can grab the latest SDK code samples right from Speakeasy, and merge them into your Bump.sh API documentation using OpenAPI Overlays which Speakeasy create for you.\n\nIf you don’t know what Overlays are, that’s ok, you don’t really need to. Simply, they are a list of changes that should be applied to an OpenAPI document, and Speakeasy has done the hard work of making them already, so all you need to do is grab a URL. To do this, head to the API registry and look for something like “train-travel-api-typescript-code-samples”.\n\n\n\nCheck the private/public toggle on, and copy the URL. Paste it into your browser to see how it looks, and you should see something like this:\n\n\n\nThis overlay document will show you exactly what it’s trying to do. It has a series of actions, which look for a specific target within the OpenAPI, and update the x-codeSamples with a perfect example of the SDK in action for every single operation in your OpenAPI document.\n\noverlay: 1.0.0\ninfo:\n  title: CodeSamples overlay for typescript target\n  version: 0.0.0\nactions:\n  - target: $[\"paths\"][\"/bookings\"][\"get\"]\n    update:\n      x-codeSamples:\n        - lang: typescript\n          label: bookings\n          source: |-\n            import { TrainTravelSDK } from \"train-travel-sdk\";\n\n            const trainTravelSDK = new TrainTravelSDK({\n              oAuth2: process.env[\"TRAINTRAVELSDK_O_AUTH2\"] ?? \"\",\n            });\n\n            async function run() {\n              const result = await trainTravelSDK.bookings.list({\n                page: 1,\n                limit: 10,\n              });\n\n              // Handle the result\n              console.log(result);\n            }\n\n            run();\n\n\nThe overlay will be much bigger than this, and it might not make sense to look at it directly, so lets apply it to an OpenAPI document to see how it all fits together. The Bump.sh CLI can help us out here.\n\nSwitch back to the repository where your OpenAPI document lives.\n\nbump overlay openapi.yaml \\\n  https://spec.speakeasy.com/bumpsh/bumpsh/train-travel-api-typescript-code-samples \\\n  &gt; openapi.codegen.yaml\n\n\nUpdating openapi.yaml to wherever your OpenAPI document lives, and https://spec.speakeasy.com/bumpsh/bumpsh/train-travel-api-typescript-code-samples URL to whatever URL you copied from the API Registry entry labelled with “Code Samples”, then openapi.codegen.yaml will be the output of those overlays once applied to the OpenAPI.\n\nAs always you can use Bump.sh to get a proper view of an OpenAPI document as human-readable API documentation, so lets point it at our newly overlayed document.\n\nbump preview openapi.codegen.yaml\n\n\nOpen the preview link in the CLI output and you should see something like this:\n\n\n\nAmazing! The TypeScript SDK is now integrated into the API documentation, and the consumer doesn’t have to look at cURL commands to see how they might work with the API.\n\nStep 6: Applying Code Sample Overlays on Bump.sh Deployments\n\nDeploying to Bump.sh with overlays is very similar, only instead of creating the overlay ourselves we can let the bump deploy command take care of that. Whatever continuous integration solution is being used will be fairly similar, but in GitHub Actions it look like this:\n\n# .github/workflows/bump.yml\nname: Deploy API documentation\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  deploy-doc:\n    if: ${{ github.event_name == 'push' }}\n    name: Deploy API documentation on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;your-doc-id-or-slug&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: openapi.yaml\n          overlay: https://spec.speakeasy.com/bumpsh/bumpsh/train-travel-api-typescript-code-samples\n\n\nCommit that, and you’ll be generating and deploying OpenAPI-based API Reference Documentation with the very latest version of the code samples.\n\nSee how they look here on the hosted Train Travel API documentation.\n\nNext Steps\n\nDocumenting Multiple SDKs\n\nTo generate multiple SDK languages just repeat the process, to create another SDK on Speakeasy, with another repository to contain it, and get that repository automated in the same way.\n\nThen in the “central repository” containing the OpenAPI update your GitHub Action to pass in multiple overlays, one for each SDK language, with a comma separating each one.\n\n  - name: Deploy API documentation\n    uses: bump-sh/github-action@v1\n    with:\n      doc: &lt;your-doc-id-or-slug&gt;\n      token: ${{secrets.BUMP_TOKEN}}\n      file: openapi.yaml\n      overlay: \"https://spec.speakeasy.com/bumpsh/bumpsh/train-travel-api-typescript-code-samples,https://spec.speakeasy.com/bumpsh/bumpsh/train-travel-api-php-code-samples\"\n\n\nWhen using any other continuous integration, the CLI deploy command can be passed multiple overlay documents by repeating the --overlay parameter.\n\n  - name: Deploy API documentation\n    run: |\n      npx bump-cli deploy openapi.yaml \\\n        --doc &lt;your-doc-id-or-slug&gt; \\\n        --token \"${{secrets.BUMP_TOKEN}}\" \\\n        --overlay https://spec.speakeasy.com/bumpsh/bumpsh/train-travel-api-typescript-code-samples \\\n        --overlay https://spec.speakeasy.com/bumpsh/bumpsh/train-travel-api-php-code-samples\n\n\nThese overlay documents will be applied in order, and you can combine them with other overlays from technical writers or other tooling.\n\nIncluding SDK Setup Instructions\n\nIf you’d like to add some extra documentation about how to install these SDKs for your users, check out our guides on Topics, or update the existing OpenAPI with overlays of your own."
        },
        {
          "id": "guides-asyncapi-cheatsheet",
          "title": "AsyncAPI 3.0 - The Cheat Sheet",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "AsyncAPI",
          "tags": "",
          "url": "/guides/asyncapi/cheatsheet/",
          "content": "AsyncAPI, as per the official documentation site, is an open source initiative that seeks to improve the current state of Event-Driven Architectures (EDA). Our long-term goal is to make working with EDAs as easy as working with REST APIs. That goes from documentation to code generation, from discovery to event management, and beyond.\n\nTheir sites offers extensive guides and tutorials, as well as the reference documentation of the AsyncAPI specification.\n\nAs we have built one for OpenAPI, we wanted to write our own AsyncAPI Cheat Sheet:\n\n\nDownload the PDF version\n\nThe Cheat Sheet is presented here in an initial version. We decided to have examples only in JSON Schema and Avro, which felt to us covering the majority of use cases we saw from Bump.sh users, at least.\n\nHere are the current sections:\n\n  Document Structure\n  General Information\n  Security\n  Channels\n  Operations\n  Messages\n  Schemas\n  Protocal Bindings\n  Reuse Elements\n  Polymorphism\n\n\nFor any feedback and suggestions, please open an issue on our GitHub Repository. We are currently working on building a web version of the Cheat Sheet, so that anyone can directly contribute to it with a Pull Request."
        },
        {
          "id": "guides-api-basics-dev-guide-api-design-first",
          "title": "A Developer's Guide to API Design-First",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "API Basics",
          "tags": "",
          "url": "/guides/api-basics/dev-guide-api-design-first/",
          "content": "API Design-First, also known as “schema-first” or “contract-first”, is all about designing the interface of an API before writing any code. It’s about planning the API contract first, and defining what the API does and how it works so everyone’s on the same page before implementation starts. This approach has been around for a while, and over time, it’s evolved to meet the needs of different technologies. These days, OpenAPI has become the defacto standard for designing REST APIs, and AsyncAPI has become the defacto standard for describing event-driven APIs. This workflow can make life easier for everyone the whole way through the lifecycle of an API.\n\nA Brief History\n\nTo understand why design-first is such a big deal, it helps to know where it came from.\n\n\n  \n    The early days: WSDL and SOAP: In the late 90s and early 2000s, SOAP web services were popular, and people used the XML-based WSDL (Web Services Description Language) to describe them. WSDL files outlined all the operations and payloads in a verbose XML format. It was powerful, but many felt it was overly complex. Developers creating, maintaining, and using the APIs felt the frustration of working with them.\n  \n  \n    REST APIs take over: REST, introduced in the early 2000s, focused on simplicity and scalability. Unlike SOAP, REST didn’t have a standard way of describing APIs. Many developers relied on manual documentation, example cURL commands, or tools like Postman collections. It worked, but it was messy, inconsistent, and could easily diverge from the implementation of the API.\n  \n  \n    The OpenAPI era: Around 2011, Swagger (later renamed OpenAPI) arrived to help describe REST APIs. It introduced a standard machine-readable format for defining operations, parameters, payloads, and validation rules, all using JSON or YAML. There were a few other similar projects (mainly RAML, API Blueprint) but they all fell out of use, and OpenAPI became the champion, especially with v3.0 and v3.1 improved on this base.\n  \n  \n    AsyncAPI: In 2017, AsyncAPI released a v1.0 of a new specification to help event-driven architectures describe their APIs in a similar way to OpenAPI, in fact it was a fork. It includes support for common message brokers such as Apache Kafka and RabbitMQ amongst many others.\n  \n\n\nWhat Is API Design-First?\n\nAPI Design-First means you define the API’s contract before writing any application code. This contract includes things like:\n\n\n  The endpoints (URLs) and their HTTP methods (GET, POST, etc.)\n  The structure and validation rules of resources and collections.\n  Authentication rules (like API keys, OAuth 2.x, OpenID).\n  Errors that could be expected, and an example of their structure.\n\n\nAt first this all seems like extra work, but much like writing tests for an application, it will eventually speed up the delivery of APIs, saving everyone time and money, and reduce costly rewrites onces not-quite-right APIs make it to beta, or even worse get into production.\n\nWhy Design-First?\n\nHere’s why design-first is worth the effort:\n\n\n  Clear communication: Everyone – from frontend developers to testers to external users – knows what the API does.\n  Parallel work: Frontend and backend teams can work at the same time. Mock APIs can be set up using the design, so development doesn’t have to wait.\n  Consistency: It’s easier to enforce standards when the contract is agreed upon first.\n  Automation: You can auto-generate documentation, code snippets, and even parts of the implementation using tools.\n  Version control: It’s easier to track and manage changes to the API over time.\n\n\nAPI Description Formats and How They Help\n\nDescribing an API is the most important part of the API design-first workflow after planning is done, and for anyone building REST/RESTish APIs, the API description format of choice is OpenAPI. For anyone working with event-driven architectures the format of choice is AsyncAPI.\n\nOpenAPI documents are written in JSON or YAML, making them machine-readable, and somewhat human-readable too. They contain all the information needed to describe the interface of an API: requests, responses, reusable components, etc.\n\nopenapi: 3.0.0\ninfo:\n  title: Example API\n  version: 1.0.0\npaths:\n  /users:\n    get:\n      summary: Get a list of users\n      responses:\n        '200':\n          description: A list of users\n          content:\n            application/json:\n              schema:\n                type: array\n                items:\n                  type: object\n                  properties:\n                    id:\n                      type: integer\n                    name:\n                      type: string\n\n\nThis snippet describes an API endpoint /users that responds with a list of users when a client sends a GET request. It then describes the responses a client could expect to see, with the status codes (e.g.: 200), content types (e.g.: application/json), then gets stuck into the schema which will outline the shape of the JSON.\n\nIf you’re just getting started with OpenAPI, we’re here to help you on your journey. We’ve put together a guide to help you learn OpenAPI from scratch, starting from the basic structure and going through every part of the functionality.\n\nAsyncAPI works in a very similar way, but instead of describing endpoints you describe “publishers” and “consumers”.\n\nasyncapi: 3.0.0\n\nchannels:\n  user/signedup:\n    address: user/signedup\n    messages:\n      publishUserSignedUp.message:\n        $ref: '#/components/messages/userSignedUp'\n\noperations:\n  publishUserSignedUp:\n    action: send\n    channel:\n      $ref: '#/channels/user~1signedup'\n    messages:\n      - $ref: '#/channels/user~1signedup/messages/publishUserSignedUp.message'\n\n\nComparing Design-first and Code-first\n\nFor years the API Code-first approach was the way to build an API. You’d sketch out the API you want to build on a whiteboard, then before that was even done somebody would be generating controllers and views in their favourite programming language and firing JSON around. The goal was always to get coding as fast as possible, so that clients could start integrating with it as soon as a prototype was ready.\n\nThe rush to get coding often meant the first version clients get to see is not really anything like what they want, so a lot of time gets lost and wasted recoding controllers and doing database migrations. At some point everyone runs out of time and they have to go to production with whatever they have, even if it’s a mess for clients to work with, and everyone just agrees to fix it all later in v2.0…\n\nFor example, when OpenAPI is utilized in this approach, it is usually as annotations or code comments, popped into the application somewhere near the code it’s describing, with the hope being that a developer will remember to update both at the same time. These annotations can then be exported to an openapi.yaml document which can be displayed as documentation or generate SDKs.\n\nclass UserController {\n  @OpenApi(\n      path = \"/users\",\n      method = HttpMethod.POST,\n      // ...\n  )\n  public static void createUser(Context ctx) {\n      // ...\n  }\n}\n\n\nSadly this approach relies entirely on conflating proximity with accuracy. The annotations and code just a few lines below would often tell two completely different stories.\n\nAnyone who has been building APIs for more than a few years has probably done this and felt the pain, which is why so many API teams are starting to leverage the API design-first workflow.\n\nHere’s a quick look at the two workflows for comparison.\n\n\n\nWhilst there are a few more steps, the time invested on agreeing a contract early on brings massive time benefits through the rest of the API lifecycle.\n\nCombining the API-Design-first workflow with OpenAPI/AsyncAPI specifically allows for amazing benefits:\n\n\n  Readable by humans and machines: The YAML/JSON format means it’s clear for developers and allows for API design reviews / governance with teams that don’t have to read multiple programming languages.\n  Interactive docs: API Documentation generators like Bump.sh turn OpenAPI/AsyncAPI documents into interactive documentation, showing off parameters and examples, so clients can quickly and easily work with the API.\n  Mock servers: Tools like Microcks and Wiretap can use the API descriptions to simulate the API, allowing parallel development of API and client applications, and allowing feedback to come in early and often.\n  Server-side Validation: Instead of rewriting all of your validation logic in docs and code, you can use the API descriptions to power your application, making absolute certain the the documentation matches the implementation and reducing time spent writing code.\n  Contract Testing: Use automated tools to probe your API implementation based off the API descriptions, and add assertions to existing test suites saying “does this response match what it says in the API description”, further ensuring the two are in agreement and saving time writing complicated contract testing by hand.\n  Code generation: Many tools generate client libraries or server stubs directly from an OpenAPI/AsyncAPI document, saving loads of time.\n  API Style Guides: Style guides are hard to enforce against code, developers need to check them manually, but with OpenAPI/AsyncAPI you can enforce standards on the API from the very first endpoint that is described.\n\n\nAnyone who has written API documentation by hand knows that it takes forever and is usually bad and outdated very quickly, so the fact that you have entirely accurate documentation from the start is a huge benefit for most teams.\n\nThese other benefits may not have ever been considered, they were just things that you spent infinite time doing by hand and had never even considered automating, but when you combine them altogether in a single workflow your team becomes unstoppable.\n\nSpeed and accuracy both go through the roof, reducing time, cost and client frustration with your API.\n\nTypeSpec Making OpenAPI Easier\n\nIf you’re looking at this thinking “I want all of those benefits, but writing up a lot of YAML sounds annoying” then take a look at TypeSpec. Released by Microsoft, TypeSpec is a TypeScript-based DSL (Domain-Specific Language) for designing HTTP APIs.\n\nThe main goal of TypeSpec is to split the language used for “design” and “description” in two. The design phase is more about ideating and things change quicker, and the description is more of an artifact of that process, but OpenAPI was essentially one language for both.\n\nOpenAPI is more verbose than any DSL could be, because it’s written in JSON/YAML and that has limitations. You end up with a lot of text files, and the more you split your API description into multiple documents, the trickier it can be to rename things and keep all references up to date.\n\nHaving the design phase handled with TypeScript allows rapid change to the whole model, with autocomplete, bulk renaming, and type-strict modelling of all your data.\n\nLater when it comes time to deploy documentation, run mock servers, do security checks, lint with style guides, etc. then TypeSpec does not have anywhere near as much tooling as OpenAPI, so you can say “ok, that design looks good, export OpenAPI” and run it through all of those tools, getting the best of both worlds.\n\nWrapping Up\n\nAPI Design-First is all about getting the API’s design nailed down before jumping into coding. It helps teams work faster, stay consistent, and avoid costly mistakes later on. OpenAPI has become the standard for REST APIs, making it easy to design, document, and manage APIs. AsyncAPI brings this same power to the event-driven API world. Tooling has evolved massively in the last few years to support these standards, so you aren’t constantly having to convert things into multiple formats or try to duct-tape infinite tools together with no common source of truth."
        },
        {
          "id": "guides-bump-sh-tutorials-feedback",
          "title": "Improve Your API with User Feedback",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Bump-sh tutorials",
          "tags": "",
          "url": "/guides/bump-sh-tutorials/feedback/",
          "content": "Gathering user feedback is essential to improving your API and ensuring it meets users’ needs. With Bump.sh, you can integrate feedback forms directly into your OpenAPI documentation by embedding a link to a form using x-feedbackLink extension.\n\nThis guide will show you how to set up a feedback form using Google Forms or Typeform and embed it seamlessly into your API documentation.\n\nStep 1: Create Your Feedback Form\n\nFirst, choose your preferred form building tool, such as Google Forms, Typeform, or even Notion. All of these options are excellent and there are many more, but here’s a quick overview:\n\n\n  Google Forms is free, simple, and perfect for straightforward surveys.\n  Typeform offers a more visually engaging experience and may work better if you want an interactive form with a modern look.\n  Notion has a new Form builder, and seeing as loads of people already run their whole life through Notion, this might be the easiest option for many.\n\n\nOnce you’ve chosen a tool:\n\n\n  For Google Forms: Go to Google Forms, and either start from a blank form or choose a template. Create questions to capture feedback on your API, like user satisfaction, feature requests, or any issues they’ve encountered.\n  For Typeform: Head to Typeform, log in, and click “Create a typeform.” Set up your questions, keeping them focused on the feedback you need to improve your API.\n  For Notion: Go to Notion, and once you have a workspace, add a New Page somewhere, and look for the “Form” option under “Getting started…”\n\n\nThe tools all have a slightly different approach to form building, but just to illustrate the idea, the Notion builder looks like this.\n\n\n\n\n  In Google Forms: Click the “Send” button, select the link icon, and copy the link (something like https://forms.gle/XYZ).\n  In Typeform: Click “Share” at the top, then copy the link (typically https://yourformname.typeform.com/to/XYZ).\n  In Notion: Click “Share Form”, then copy the link (https://www.notion.so/bumpsh/XYZ)\n\n\n\n\nNow that you have the link to your form, we can add it to your API documentation.\n\nStep 2: Embed the Feedback Form Link Using x-feedbackLink\n\nWith your form link copied, open your OpenAPI document (usually openapi.yaml or openapi.json).\n\nAdd the x-feedbackLink property under the info section and paste in your feedback form link, using this example OpenAPI\n\nHere’s an example of what this might look like in your OpenAPI YAML file:\n\nopenapi: 3.1.0\ninfo:\n  title: Your Amazing API\n  version: 1.0.0\n  description: \"API documentation with integrated feedback form\"\n  x-feedbackLink:\n    label: Give feedback\n    url: \"https://www.notion.so/bumpsh/133539284334765834653324\" \npaths:\n  # ...\n\n\nClick save, then deploy Bump.sh API Documentation again however you normally deploy it: CLI, CI, API, or manually through the website.\n\nStep 3: Check the Feedback Link in API Documentation\n\nNow, go to your API documentation on Bump.sh to confirm the feedback form is embedded correctly. You should see a “Give feedback” link up at the top of the API documentation, or it will use whatever other text you provided in label.\n\n\n\nClick the link that has appeared, and try sending a test submission to make sure it works as expected. When you do this it’s best to do it in a private window to flush out any odd permissions issues, because you want to emulate an end-user, and they won’t be logged in to the form builder software either.\n\nAlternative Idea: GitHub Issues as Feedback\n\nIf you’re using GitHub, and the audience using the API have access to this repository, you might want to consider using GitHub Issue Templates instead of a custom form on one of these other services.\n\nGitHub has its own form builder which you can use to create something very similar.\n\n\n\nCreate a new file .github/ISSUE_TEMPLATE/feedback.yaml, and use this example to start you off:\n\nname: Feedback\ndescription: Help us improve this API with constructive feedback.\ntitle: \"[Feedback]: \"\nlabels: [\"feedback\"]\nassignees:\n  - philsturgeon # change this to whoever should be in charge of handling feedback\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        Thanks for taking the time to help us improve!\n  - type: textarea\n    id: doing-well\n    attributes:\n      label: What are we doing well?\n      description: Help us know where energy has been well invested so we can keep that up.\n    validations:\n      required: true\n  - type: textarea\n    id: could-improve\n    attributes:\n      label: What would you like to see improved?\n      description: Help us learn what you'd like to see done better so we can make life easier for more users.\n    validations:\n      required: true\n  - type: input\n    id: contact\n    attributes:\n      label: Contact Details\n      description: How can we get in touch with you if we need more info?\n      placeholder: \"eg: email@example.com\"\n    validations:\n      required: false\n\n\nWhen you have committed that and pushed it to your default branch (probably main), you can go to GitHub &gt; Issues &gt; New, and the Feedback option should have appeared.\n\nCopy this URL out of your browsers’ address bar, e.g.: https://github.com/bump-sh-examples/train-travel-api/issues/new/choose. Paste that URL into x-feedbackLink.url:\n\nopenapi: 3.1.0\ninfo:\n  title: Your Amazing API\n  version: 1.0.0\n  description: \"API documentation with integrated feedback form\"\n  x-feedbackLink:\n    label: Give feedback\n    url: \"https://github.com/bump-sh-examples/train-travel-api/issues/new/choose\"\n\n\nThat’s it!\n\nTips for Effective Feedback Collection\n\nKeep it short and focused\n\nA short form is more likely to get responses, so focus on key questions:\n\n\n  “What are we doing well?” - Textbox\n  “What do we need to improve?” - Textbox\n  “How would you rate your experience with this API?” Checkbox/Slider 1-5\n\n\nContact details should be optional, because sometimes people want to let you know things are terrible without having their name attached, and sometimes that’s valid. You can always make it required later if malicious users are abusing the system.\n\nReview responses regularly\n\nRegularly check responses to understand user needs and make timely improvements. If something is broken you might only hear about it from one user, but there are likely 10-100 users who didn’t take the time to provide feedback. That one user who did is valuable, and you should show them that with a quick response, and a quick fix."
        },
        {
          "id": "guides-openapi-accelerating-youropenapi-spec-generation-with-typespec",
          "title": "Accelerating your OpenAPI Spec Generation with TypeSpec",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "OpenAPI",
          "tags": "",
          "url": "/guides/openapi/accelerating-youropenapi-spec-generation-with-typespec/",
          "content": "There have been a number of options emerge recently beyond OpenAPI for documenting your API interface details. One such option is TypeSpec, which offers a declarative syntax to define HTTP, REST, and gRPC-based APIs. The high-level syntax allows developers to define API types, schemas, and interfaces, making it easier to manage the complexities of modern API ecosystems.\n\nIn this article, we will explore what TypeSpec is, how it can transform your API documentation process, and how it supports established API standards like OpenAPI and gRPC. By leveraging TypeSpec, teams can streamline the creation and maintenance of their APIs, ensuring that their systems remain consistent and scalable across various services. Along the way, we will dive into examples, practical applications, and find out where TypeSpec fits into the API lifecycle to help you decide if it’s the right tool for your API program.\n\nWhat is TypeSpec?\n\nTypeSpec (formerly known as Cadl) was built to simplify the API design process for REST APIs, HTTP services, and gRPC APIs by providing a high-level, declarative syntax to model types and services. It supports defining the types, schemas, and interfaces that describe an API’s operations and resource models.\n\nBy using TypeSpec, developers can generate code, documentation, and other artifacts from their API definitions, simplifying the process of maintaining and evolving APIs and services. Microsoft leverages TypeSpec internally to define APIs across various products and services, including Azure.\n\nAs of this writing, TypeSpec supports the modeling of REST and HTTP APIs, gRPC, and JSON Schema. This is handled by the use of decorators, which provide annotation-style declarations of your resources and operations. Emitters are then used to transform TypeSpec into the preferred output format for the model used including OpenAPI v3 for REST and HTTP APIs, an IDL file for gRPC, or JSON Schema. This means that you are able to use TypeSpec tooling to describe your APIs, while still leveraging existing tools for the remainder of your API delivery lifecycle.\n\nGetting Started with TypeSpec\n\nYou can install TypeSpec in just a few steps using the installation instructions provided by the Getting Started Guide. At a minimum, you will need a current version of Node.js installed, along with the TypeSpec CLI.\n\nIf you prefer, you can use the TypeSpec Playground to get started quickly, without the need to install the CLI.\n\nA TypeSpec Example\n\nThe first step is to create a new TSP file or use an existing TSP file. A TSP file contains your API definition using TypeSpec syntax. Below is an example provided by the TypeSpec documentation for a Petstore API:\n\nimport \"@typespec/http\";\n\nusing TypeSpec.Http;\n\n@service({\n  title: \"Pet Store\",\n})\n@server(\"https://example.com\", \"Single server endpoint\")\nnamespace PetStore;\n\nmodel Pet {\n  id: int32;\n\n  @minLength(1)\n  name: string;\n\n  @minValue(0)\n  @maxValue(100)\n  age: int32;\n\n  kind: petType;\n}\n\nenum petType {\n  dog: \"dog\",\n  cat: \"cat\",\n  fish: \"fish\",\n  bird: \"bird\",\n  reptile: \"reptile\",\n}\n\n@route(\"/pets\")\nnamespace Pets {\n  @get\n  op listPets(): {\n    @statusCode statusCode: 200;\n    @body pets: Pet[];\n  };\n\n  @get\n  op getPet(@path petId: int32): {\n    @statusCode statusCode: 200;\n    @body pet: Pet;\n  } | {\n    @statusCode statusCode: 404;\n  };\n\n  @post\n  op createPet(@body pet: Pet): {\n    @statusCode statusCode: 201;\n    @body newPet: Pet;\n  } | {\n    @statusCode statusCode: 202;\n    @body acceptedPet: Pet;\n  };\n\n  @put\n  op updatePet(@path petId: int32, @body pet: Pet): {\n    @statusCode statusCode: 200;\n    @body updatedPet: Pet;\n  } | {\n    @statusCode statusCode: 404;\n  };\n\n  @delete\n  op deletePet(@path petId: int32): {\n    @statusCode statusCode: 204;\n  };\n}\n\nThe import and Using commands tell the TypeSpec compiler what kind of decorators you will be using to define your API. In this case, we will be modeling a REST/HTTP API.\n\nWe then define the details about the API using the @service decorator and the host(s) where our API implementation will reside using the @servers decorator.\n\nNext, we use TypeSpec model and enum declarations to describe our models. For this example, we have a Pet resource model that our API will support.\n\nFinally, we define our API operations using a @route decorator that will define the path and associated HTTP methods our API supports. In this case, we offer @get to retrieve all Pets or a specific Pet by ID, @post to add a Pet to the pet store, @put to update a Pet, and @delete to delete a Pet.\n\nNotice that the syntax is designed to optimize the developer experience when defining your API definition. In our example, a single declaration of the route /pets sets the current URL path. Within this route, we see that each HTTP method builds upon this path to define GET /pets, GET /pets/{petId}, and so on. This makes for a much more manageable API definition than a traditional OpenAPI definition.\n\nUsing the TypeSpec playground, I converted the example above into an OpenAPI Specification:\n\nopenapi: 3.0.0\ninfo:\n title: Pet Store\n version: 0.0.0\ntags: []\npaths:\n /pets:\n   get:\n     operationId: Pets_listPets\n     parameters: []\n     responses:\n       '200':\n         description: The request has succeeded.\n         content:\n           application/json:\n             schema:\n               type: array\n               items:\n                 $ref: '#/components/schemas/Pet'\n   post:\n     operationId: Pets_createPet\n     parameters: []\n     responses:\n       '201':\n         description: The request has succeeded and a new resource has been created as a result.\n         content:\n           application/json:\n             schema:\n               $ref: '#/components/schemas/Pet'\n       '202':\n         description: The request has been accepted for processing, but processing has not yet completed.\n         content:\n           application/json:\n             schema:\n               $ref: '#/components/schemas/Pet'\n     requestBody:\n       required: true\n       content:\n         application/json:\n           schema:\n             $ref: '#/components/schemas/Pet'\n /pets/{petId}:\n   get:\n     operationId: Pets_getPet\n     parameters:\n       - name: petId\n         in: path\n         required: true\n         schema:\n           type: integer\n           format: int32\n     responses:\n       '200':\n         description: The request has succeeded.\n         content:\n           application/json:\n             schema:\n               $ref: '#/components/schemas/Pet'\n       '404':\n         description: The server cannot find the requested resource.\n   put:\n     operationId: Pets_updatePet\n     parameters:\n       - name: petId\n         in: path\n         required: true\n         schema:\n           type: integer\n           format: int32\n     responses:\n       '200':\n         description: The request has succeeded.\n         content:\n           application/json:\n             schema:\n               $ref: '#/components/schemas/Pet'\n       '404':\n         description: The server cannot find the requested resource.\n     requestBody:\n       required: true\n       content:\n         application/json:\n           schema:\n             $ref: '#/components/schemas/Pet'\n   delete:\n     operationId: Pets_deletePet\n     parameters:\n       - name: petId\n         in: path\n         required: true\n         schema:\n           type: integer\n           format: int32\n     responses:\n       '204':\n         description: 'There is no content to send for this request, but the headers may be useful. '\ncomponents:\n schemas:\n   Pet:\n     type: object\n     required:\n       - id\n       - name\n       - age\n       - kind\n     properties:\n       id:\n         type: integer\n         format: int32\n       name:\n         type: string\n         minLength: 1\n       age:\n         type: integer\n         format: int32\n         minimum: 0\n         maximum: 100\n       kind:\n         $ref: '#/components/schemas/petType'\n   petType:\n     type: string\n     enum:\n       - dog\n       - cat\n       - fish\n       - bird\n       - reptile\nservers:\n - url: https://example.com\n   description: Single server endpoint\n   variables: {}\n\n\nAs you can see, TypeSpec is much smaller and easier to manage while the OpenAPI format is about twice as long (69 lines for TypeSpec vs. 131 lines for OpenAPI). Of course, we could continue to extend our TypeSpec definition with our own operationId values rather than using the auto-generated ones, examples that will be mapped to OpenAPI examples, and a variety of other OpenAPI options. Considerable work has been done by the TypeSpec team to make the emitters production ready and as close to the OpenAPI, gRPC IDL, and JSON Schema specifications as possible.\n\nShould I Use TypeSpec or OpenAPI?\n\nTypeSpec offers several advantages over OpenAPI:\n\n\n  \n    Type-Centric Language: TypeSpec emphasizes defining API models and associated types. These models can then be reused across different APIs, making it easier to maintain consistency in larger API ecosystems.\n  \n  \n    Schema Definition: It provides a way to define data schemas and validation rules, which can later be transformed into various output formats, such as OpenAPI, JSON Schema, and Protocol Buffers.\n  \n  \n    API First Design: TypeSpec encourages API-first design principles, meaning you define the API contract before the implementation, ensuring that both the client and server agree on the interface.\n  \n  \n    Target Multiple Outputs: One of the primary advantages of TypeSpec is its ability to generate multiple output formats from a single source of truth. It can produce OpenAPI, GraphQL schemas, or even generate code for client libraries or server implementations.\n  \n  \n    Extensibility: TypeSpec is designed to be extensible, allowing users to write plugins to customize the output format or introduce new conventions for specific platforms.\n  \n\n\nThis makes TypeSpec a powerful tool for organizations focused on API standardization, consistency, and automation, fitting well into environments where multiple APIs and services need to coexist under shared design principles.\n\nHowever, it is important to keep in mind that TypeSpec is still a bit early. While used heavily by Microsoft and other organizations, the tool ecosystem is limited. So, expect to add a step in your delivery pipeline to convert your TypeSpec file into OpenAPI so that you can continue to use your favorite OpenAPI-based tools.\n\nWhere Does TypeSpec Fit in the API Lifecycle?\n\nTypeSpec assumes that you have already modeled your APIs using an API design first methodology, such as the Align-Define-Design-Refine (ADDR), and are ready to apply an API design style. However, TypeSpec does offer advantages in the design, delivery, and documentation stages of the API lifecycle:\n\n\n  API Contract Definition: In an API-first development model, defining the API contract is crucial. TypeSpec provides a high-level, type-centric language for defining the interface and structure of your API. It allows teams to describe the types, methods, and services, ensuring clarity about what the API should do and what data it will handle.\n  Consistency &amp; Reusability: TypeSpec promotes consistency across different APIs within an organization by allowing reusable types and schemas. This is particularly important for teams working with multiple APIs that need to adhere to shared standards and practices.\n  Standardized Documentation: TypeSpec can generate documentation from the API definition (e.g., OpenAPI/Swagger documentation), allowing you to import it into Bump.sh and other tools\n  API Versioning: TypeSpec facilitates API versioning by allowing different versions of an API to be defined and managed from the same source. This is essential when evolving APIs while maintaining backward compatibility with older clients.\n  Schema Evolution: As an API evolves, changes to the schema and types can be managed within TypeSpec, ensuring that changes are tracked and understood across teams.\n  Governance: For organizations that have centralized API governance, TypeSpec can help enforce consistency in API designs across different teams by enforcing organizational standards for API design.\n  Automated Linting and Checks: TypeSpec can be integrated into CI/CD pipelines to automatically check whether APIs conform to best practices and organizational rules during the design phase.\n\n\nWrap-Up\n\nTypeSpec has some considerable potential. While I was hoping to find a way to model APIs using the ADDR API Profile format prior to mapping it into a specific API design style, it offers a simple way to capture your API design using a single syntax and then convert it into the format required for your tooling. Organizations with a large API portfolio would also benefit from the ability to modularize, share, and import your definitions easily, prior to outputting a single OpenAPI definition.\n\nI would recommend checking out the getting started guide to help you understand the full potential of TypeSpec. Considerable work has been done to make the tooling approachable, with many examples including the one we used above.\n\nJust keep in mind that the TypeSpec syntax is still developer-centric, so product owners and business users may find it difficult to use unless they have a development background. However, technical audiences such as developers and technical writers might find the syntax useful for describing APIs, then using the OpenAPI emitter to convert TypeSpec to OpenAPI for use by their CI/CD toolchain."
        },
        {
          "id": "guides-openapi-writing-an-openapi-document-from-the-ground-up",
          "title": "Writing an OpenAPI Document from the Ground Up",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "OpenAPI",
          "tags": "",
          "url": "/guides/openapi/writing-an-openapi-document-from-the-ground-up/",
          "content": "Composing an OpenAPI document can be daunting, especially during the early stages of API design. Your API design will need to evolve as you learn more about the problem space. To avoid needing to re-write large portions of your OpenAPI document, I recommend a phased approach to designing and documenting your API. The process starts with a breadth-first approach, capturing the essentials of the API including purpose and scope, then adding high-level operational descriptions. Finally, add more detail as you gain insights and receive feedback, until your OpenAPI document is complete.\n\nIn this article, we will break the OpenAPI documentation process into three phases, with steps within each phase to help you get the most out of your document. Each phase will keep your OpenAPI document syntactically valid for input to your tool chain. To see the process in action, we will use an example TODO list API to show how you can iteratively produce your OpenAPI document as you gain a deeper understanding of the needs of your developers.\n\nIntroducing the TODO API\n\nTo understand the phased approach, let’s use a simple TODO API example. The API will support the following operations:\n\n\n  Add a new task - POST /tasks\n  View a task - GET /tasks/{taskId}\n  Update a task - PUT /tasks/{taskId}\n  Delete a task - DELETE /tasks/{taskId}\n  List tasks - GET /tasks\n\n\nWhile we could add more operations, this API will provide a good sample of operations to better understand how to iteratively compose our OpenAPI documentation. Let’s get started by first capturing the info section.\n\nIf you are new to the OpenAPI Specification, check out our article titled, “What is OpenAPI?” to gain better understanding prior to jumping into this step-by-step guide.\n\nLooking for an OpenAPI reference? Be sure to check out the OpenAPI Cheat Sheet.\n\nPhase 1: Create the Info Section\n\nThe info block is crucial as it contains essential metadata about your API, including the title, description, version, and terms of service. It helps readers quickly understand what your API does and the terms associated with its use.\n\nStep 1.1: Define the API Title\n\n\n  Objective: Create a descriptive and succinct title that is SEO-friendly.\n  Guidelines:\n    \n      Be descriptive enough so that the reader immediately understands what the API offers.\n      Avoid generic and vague terms like ‘service’, ‘manager’, and ‘controller’, which do not provide insight into the API’s functionality and leaks implementation details.\n    \n  \n  Actions:\n    \n      Create the boilerplate of the document, including openapi and info sections\n      Add a thoughtful title using the guidelines above\n    \n  \n\n\nExample:\n\nopenapi: 3.0.0\ninfo:\n  title: Personal TODO List API\n  version: 1.0.0\n\n\nStep 1.2: Write a Compelling API Description\n\n\n  Objective: Offer a clear summary of what the API does, its scope, and capabilities.\n  Actions: Add a description of the API\n  Tips:\n    \n      Include purpose and scope to clarify the API’s intent.\n      Mention key digital capabilities to outline what the API can do.\n      Highlight the workflow or typical use cases to guide the reader on how to implement the API effectively.\n      Utilize Markdown for formatting to enhance readability and structure of the content.\n    \n  \n\n\nExample:\n\nopenapi: 3.0.0\ninfo:\n  title: Personal TODO List API\n  version: 1.0.0\n  description: \n    This API allows an individual to manage a simple list of tasks. \n    \n    Tasks have a status of 'new' when first created and should be moved to 'in progress' when the task has begun and 'completed' once the task is finished.\n\n\nStep 1.3: Include Additional Metadata\n\n  Objective: Add additional metadata and a boilerplate section to ensure the document is syntactically valid.\n  Actions: Add the following sections:\n    \n      Contact Details: Add relevant contact information like an email, phone number, or a link to your API support or contact page.\n      Licensing Details: Specify the API’s license type and include a link to the full license text if available.\n      Version: Note the current version of the API document, which is important for users to track changes and updates.\n      Servers: If you know the URL(s) for your servers, go ahead and put them here. Otherwise, you can add them later.\n      Paths: Since we focused on the info section for now, let’s create an empty paths section to ensure the file will validate without any errors.\n    \n  \n\n\nExample:\n\nopenapi: 3.0.0\ninfo:\n  title: Personal TODO List API\n  version: 1.0.0\n  description: \n    This API allows an individual to manage a simple list of tasks. \n    \n    Tasks have a status of 'new' when first created and should be moved to 'in progress' when the task has begun and 'completed' once the task is finished.\n  contact:\n    email: api-support@todolist.local\n  license:\n    name: Apache 2.0\n    url: https://www.apache.org/licenses/LICENSE-2.0.html\n  termsOfService: https://onlinestore.com/terms    \npaths: {}\n\n\nStep 1.4: Review and Revise\n\n  Objective: Ensure clarity, accuracy, and effectiveness of the information provided.\n  Action: Review the info section with stakeholders, revise based on feedback, and keep it updated as changes occur in services or terms. Validate that the OpenAPI document is valid using your favorite editor or the Swagger Editor as a quick check.\n\n\nFor more information on improving your info section, refer to the article titled, “Enriching Your OpenAPI Info Documentation for Understanding”\n\nPhase 2: Capture High-Level Operation Details\n\nNext, let’s capture some basic operation details. This will include the paths and HTTP methods our API will offer, along with some summary and description details. We will expand the operations further in the final phase. For now, focus on identifying all of the operations you will need to capture.\n\n\n  You may be thinking that the next step is to capture schema components. However, I’ve found that taking a breadth-based approach to the operations first offers a better opportunity to identify all necessary schema components without getting too deep too quickly.\n\n\nStep 2.1: Capture Paths and HTTP Methods\n\n\n  Objective: Capture the basic framework of API operations before adding details.\n  Action: Focus on capturing all API operations using only the paths and HTTP methods. Additional details will be added later.\n  Guidelines:\n    \n      Each operation is specified under the paths object in the OAS.\n      Operations are defined by HTTP methods like GET, POST, PUT, PATCH, DELETE, etc.\n    \n  \n\n\nExample:\n\npaths:\n  /tasks:\n    get:\n    post:\n  /tasks/{taskId}:\n    get:\n    delete:\n    put:\n\n\nStep 2.2: Capture Operation Summaries and Descriptions\n\n  Objective: Document the purpose and details of each API operation.\n  Actions:\n    \n      Write a short summary about the purpose of each operation, along with a more detailed description.\n      Add an operationId to provide a unique name to each operation which can be useful for linking and clarity.\n    \n  \n  Tips:\n    \n      The summary of each operation will be the first thing the reader sees, so spend time crafting a thoughtful summary.\n      The description should provide further context on when (and when not) to use the specific operation to give the reader confidence that they are in the right place.\n      Utilize Markdown for formatting the descriptions to include bold, italics, bullet points, and links.\n      Be descriptive and avoid generic phrases; provide enough context to understand the operation’s utility and execution.\n    \n  \n\n\nExample:\n\npaths:\n  /tasks:\n    get:\n      summary: List available tasks on the TODO list\n      description: \n        Lists tasks that have been created, with a default filter of 'new' and 'in progress' tasks unless specified otherwise\n      operationId: listTasks\n    post:\n      summary: Add a new task to the TODO list\n      description:\n        Adds a new task to the TODO list and sets the status to 'new'\n      operationId: addTask\n  /tasks/{taskId}:\n    get:\n      summary: Retrieve the details for a task on the TODO list\n      description:\n        Returns the details for a task from the TODO list by identifier\n      operationId: getTask\n    delete:\n      summary: Remove a task from the TODO list\n      description:\n        Permanently removes a task from the TODO list. If a task was completed, use `PUT /tasks/{taskId}` to update its status to 'completed'\n      operationId: removeTask\n    put:\n      summary: Update a task on the TODO list\n      description:\n        Updates a specific task's details and/or status\n      operationId: updateTask\n\n\nRefer to the guide “5 Improvements to OpenAPI Operation Documentation” for tips on improving your API operation documentation.\n\nStep 2.3: Assign Tags to Group Operations\n\n  Objective: Identify a few tags that will group API operations\n  Action: Add a tags section to each API operation, along with a tags description section to name each of your tags.\n  Guidelines: If your API contains multiple operations that could be grouped, using tags helps the reader to find the operation they need quickly.\n\n\nExample:\n\ntags:\n  - name: Manage Tasks\n    description: Supports task management such as adding, updating, and deleting tasks\n  - name: View Tasks\n    description: Lists all tasks or retrieves the details for a specific task\npaths:\n  /tasks:\n    get:\n      summary: List available tasks on the TODO list\n      description: \n        Lists tasks that have been created, with a default filter of 'new' and 'in progress' tasks unless specified otherwise\n      operationId: listTasks\n      tags:\n        - View Tasks\n    post:\n      summary: Add a new task to the TODO list\n      description:\n        Adds a new task to the TODO list and sets the status to 'new'\n      operationId: addTask\n      tags:\n        - Manage Tasks\n  /tasks/{taskId}:\n    get:\n      summary: Retrieve the details for a task on the TODO list\n      description:\n        Returns the details for a task from the TODO list by identifier\n      operationId: getTask\n      tags:\n        - View Tasks\n    delete:\n      summary: Remove a task from the TODO list\n      description:\n        Permanently removes a task from the TODO list. If a task was completed, use `PUT /tasks/{taskId}` to update its status to 'completed'\n      operationId: removeTask\n      tags:\n        - Manage Tasks\n    put:\n      summary: Update a task on the TODO list\n      description:\n        Updates a specific task's details and/or status\n      operationId: updateTask\n      tags:\n        - Manage Tasks\n\n\nRefer to our guide, “Using OpenAPI and AsyncAPI Tags to Better Organize API Endpoints” for further details on using tagging in your OpenAPI description.\n\nStep 2.4: Capture Path Parameters and Query Arguments\n\n  Objective: Capture path parameter and query argument details.\n  Action: Add any path parameters and query arguments to each operation, with a clear description, type/format, and required boolean value.\n\n\nExample:\n\npaths:\n  /tasks:\n    get:\n      # ... hidden for readability ...\n      parameters: \n      - name: status\n        in: query\n        required: false\n        description: Filters the list of tasks by the status provided\n        schema:\n          type: string\n    post:\n      summary: Add a new task to the TODO list\n      # ... hidden for readability ...\n  /tasks/{taskId}:\n    parameters:\n      - name: taskId\n        in: path\n        required: true\n        description: The ID of the task to operate on\n        schema:\n          type: integer\n    get:\n      summary: Retrieve the details for a task on the TODO list\n      # ... hidden for readability ...\n    delete:\n      summary: Remove a task from the TODO list\n      # ... hidden for readability ...\n    put:\n      summary: Update a task on the TODO list\n      # ... hidden for readability ...\n\n\nStep 2.5: Document Expected Responses\n\n  Objective: Specify the success and error response codes for each operation.\n  Action: Add responses for success and error codes to each operation, including a description. The response details will be expanded in the final phase.\n\n\nExample:\n\npaths:\n  /tasks:\n    get:\n      summary: List available tasks on the TODO list\n      # ... hidden for readability ...\n      responses:\n        '200':\n          description: Returns a list of Tasks, with any request filters applied\n    post:\n      summary: Add a new task to the TODO list\n      # ... hidden for readability ...\n      responses:\n        '201':\n          description: Task created successfully\n  /tasks/{taskId}:\n    # ... hidden for readability ...\n    get:\n      summary: Retrieve the details for a task on the TODO list\n      # ... hidden for readability ...\n      responses:\n        '200':\n          description: Returns the details of the retrieved task\n    delete:\n      summary: Remove a task from the TODO list\n      # ... hidden for readability ...\n      responses:\n        '204':\n          description: Task deleted successfully\n        '404':\n          description: Task not found by the ID provided\n    put:\n      summary: Update a task on the TODO list\n      # ... hidden for readability ...\n      responses:\n        '200':\n          description: Task updated successfully\n        '404':\n          description: Task not found by the ID provided\n\n\nOf course, feel free to add more responses to your document, such as 400 when the resource representation lacks required fields or valid values when we create and update a Task.\n\nStep 2.6: Review and Validate\n\n  Objective: Ensure the accuracy and completeness of the operation documentation.\n  Action: Test each operation using your editor to ensure you have addressed any syntax errors as you review your operation overview details.\n\n\nAs a review, here is what we have captured so far in our validated OpenAPI document:\n\nopenapi: 3.0.0\ninfo:\n  title: Personal TODO List API\n  version: 1.0.0\n  description: \n    This API allows an individual to manage a simple list of tasks. \n    \n    Tasks have a status of 'new' when first created and should be moved to 'in progress' when the task has begun and 'completed' once the task is finished.\n  contact:\n    email: api-support@todolist.local\n  license:\n    name: Apache 2.0\n    url: https://www.apache.org/licenses/LICENSE-2.0.html\n  termsOfService: https://onlinestore.com/terms    \nservers: \n  - url: 'https://api.todolist.local/v1'\ntags:\n  - name: Manage Tasks\n    description: Supports task management such as adding, updating, and deleting tasks\n  - name: View Tasks\n    description: Lists all tasks or retrieves the details for a specific task\npaths:\n  /tasks:\n    get:\n      summary: List available tasks on the TODO list\n      description: \n        Lists tasks that have been created, with a default filter of 'new' and 'in progress' tasks unless specified otherwise\n      operationId: listTasks\n      tags:\n        - View Tasks\n      parameters: \n      - name: status\n        in: query\n        required: false\n        description: Filters the list of tasks by the status provided\n        schema:\n          type: string\n      responses:\n        '200':\n          description: Returns a list of Tasks, with any request filters applied\n    post:\n      summary: Add a new task to the TODO list\n      description:\n        Adds a new task to the TODO list and sets the status to 'new'\n      operationId: addTask\n      tags:\n        - Manage Tasks\n      responses:\n        '201':\n          description: Task created successfully\n  /tasks/{taskId}:\n    parameters:\n      - name: taskId\n        in: path\n        required: true\n        description: The ID of the task to operate on\n        schema:\n          type: integer\n    get:\n      summary: Retrieve the details for a task on the TODO list\n      description:\n        Returns the details for a task from the TODO list by identifier\n      operationId: getTask\n      tags:\n        - View Tasks\n      responses:\n        '200':\n          description: Returns the details of the retrieved task\n    delete:\n      summary: Remove a task from the TODO list\n      description:\n        Permanently removes a task from the TODO list. If a task was completed, use `PUT /tasks/{taskId}` to update its status to 'completed'\n      operationId: removeTask\n      tags:\n        - Manage Tasks\n      responses:\n        '204':\n          description: Task deleted successfully\n        '404':\n          description: Task not found by the ID provided\n    put:\n      summary: Update a task on the TODO list\n      description:\n        Updates a specific task's details and/or status\n      operationId: updateTask\n      tags:\n        - Manage Tasks\n      responses:\n        '200':\n          description: Task updated successfully\n        '404':\n          description: Task not found by the ID provided\n\n\nIn the final phase, we will add the final details for the documentation to make it complete.\n\nPhase 3: Capture Schema and Remaining Operation Details\n\nThis is the final pass of our documentation effort, focusing on adding schema components and any remaining operation details for our API.\n\nStep 3.1: Add Request and Response Details\n\n\n  Objective: Add request and response details, including JSON refs that will point to our future schema components.\n  Action: Add requestBody and response content to each operation’s request and response.\n  Tips:\n    \n      Add JSON refs for the schema component definitions that you will need to add. We will add those in the next step, but adding the JSON refs will help us identify what we need.\n    \n  \n\n\nExample:\n\npaths:\n  /tasks:\n    get:\n      summary: List available tasks on the TODO list\n      # ... hidden for readability ...\n      responses:\n        '200':\n          description: Returns a list of Tasks, with any request filters applied\n          content:\n            application/json:\n              schema:\n                type: array\n                items:\n                  $ref: '#/components/schemas/Task'\n    post:\n      summary: Add a new task to the TODO list\n      # ... hidden for readability ...\n      responses:\n        '201':\n          description: Task created successfully\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Task'\n  /tasks/{taskId}:\n    # ... hidden for readability ...\n    get:\n      summary: Retrieve the details for a task on the TODO list\n      # ... hidden for readability ...\n      responses:\n        '200':\n          description: Returns the details of the retrieved task\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Task'\n    delete:\n      summary: Remove a task from the TODO list\n      # ... hidden for readability ...\n      responses:\n        '204':\n          description: Task deleted successfully\n        '404':\n          description: Task not found by the ID provided\n    put:\n      summary: Update a task on the TODO list\n      # ... hidden for readability ...\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/Task'\n      responses:\n        '200':\n          description: Task updated successfully\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Task'\n        '404':\n          description: Task not found by the ID provided\n\n\nStep 3.2: Add Missing Schema Components\n\n  Objective: Specify reusable schema components that are referenced across operations.\n  Action: Add schema components referenced from the previous step, including the type, format, and other details.\n\n\nExample:\n\ncomponents:\n  schemas:\n    Task:\n      type: object\n      description: Represents a task with a status indicating its progression.\n      properties:\n        id:\n          type: integer\n          description: The unique identifier for a task.\n        description:\n          type: string\n          description: A brief summary of the task.\n        status:\n          type: string\n          description: The current status of the task, which can be 'new', 'in progress', or 'completed'.\n          enum:\n            - new\n            - in progress\n            - completed\n          default: new\n\n\nStep 3.3: Capture Operation Security Requirements\n\n  Objective: Specify the authentication and authorization requirements for each operation.\n  Action: Add required securitySchemes and the required scopes for each operation under the security field.\n\n\nExample:\n\npaths:\n  /tasks:\n    get:\n      summary: List available tasks on the TODO list\n      # ... hidden for readability ...\n      security:\n        - oauth2:\n            - task:read\n      # ... hidden for readability ...\n    post:\n      summary: Add a new task to the TODO list\n      # ... hidden for readability ...\n      security:\n        - oauth2:\n            - task:create\n      # ... hidden for readability ...\n  /tasks/{taskId}:\n    # ... hidden for readability ...\n    get:\n      summary: Retrieve the details for a task on the TODO list\n      # ... hidden for readability ...\n      security:\n        - oauth2:\n            - task:read\n      # ... hidden for readability ...\n    delete:\n      summary: Remove a task from the TODO list\n      # ... hidden for readability ...\n      security:\n        - oauth2:\n            - task:delete\n      # ... hidden for readability ...\n    put:\n      summary: Update a task on the TODO list\n      # ... hidden for readability ...\n      security:\n        - oauth2:\n            - task:update\n      # ... hidden for readability ...\ncomponents:\n  # ... hidden for readability ...\n  securitySchemes:\n    oauth2:\n      type: oauth2\n      flows:\n        authorizationCode:\n          authorizationUrl: https://api.todolist.local/oauth/authorize\n          tokenUrl: https://api.todolist.local/oauth/token\n          scopes:\n            task:read: Authorization to Read tasks\n            task:create: Authorization to Create tasks\n            task:update: Authorization to Update tasks\n            task:delete: Authorization to Delete tasks\n\nStep 3.4: Add Examples to Operations and Schema Components\n\n  Objective: Expand the documentation to include examples for better understanding.\n  Action: Add examples for schema components, request parameters, and request/response objects.\n\n\nExample:\n\npaths:\n  /tasks:\n    get:\n      summary: List available tasks on the TODO list\n      # ... hidden for readability ...\n      parameters: \n      - name: status\n        in: query\n        required: false\n        description: Filters the list of tasks by the status provided\n        schema:\n          type: string\n          example: 'completed'\n      responses:\n        '200':\n          description: Returns a list of Tasks, with any request filters applied\n          content:\n            application/json:\n              schema:\n                type: array\n                items:\n                  $ref: '#/components/schemas/Task'\n              examples:\n                tasks:\n                  value: \n                    - id: 1\n                      description: \"Buy milk\"\n                      status: \"completed\"\n                    - id: 2\n                      description: \"Send email to John\"\n                      status: \"in progress\"\n    post:\n      summary: Add a new task to the TODO list\n      # ... hidden for readability ...\n      responses:\n        '201':\n          description: Task created successfully\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Task'\n              examples:\n                task:\n                  value:\n                    id: 3\n                    description: \"Read a book\"\n                    status: \"new\"\n  /tasks/{taskId}:\n    parameters:\n      # ... hidden for readability ...\n    get:\n      summary: Retrieve the details for a task on the TODO list\n      # ... hidden for readability ...\n      responses:\n        '200':\n          description: Returns the details of the retrieved task\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Task'\n              examples:\n                task:\n                  value:\n                    id: 3\n                    description: \"Read a book\"\n                    status: \"new\"\n    delete:\n      summary: Remove a task from the TODO list\n      # ... hidden for readability ...\n    put:\n      summary: Update a task on the TODO list\n      # ... hidden for readability ...\ncomponents:\n  schemas:\n    Task:\n      type: object\n      description: Represents a task with a status indicating its progression.\n      properties:\n        id:\n          type: integer\n          description: The unique identifier for a task.\n          example: 1\n        description:\n          type: string\n          description: A brief summary of the task.\n          example: \"Buy milk\"\n        status:\n          type: string\n          description: The current status of the task, which can be 'new', 'in progress', or 'completed'.\n          enum:\n            - new\n            - in progress\n            - completed\n          default: new\n          example: \"new\"\n  securitySchemes:\n    # ... hidden for readability ...\n\n\nThis OpenAPI document example is available in the Bump.sh examples GitHub repository. Here’s a preview of how this definition would render on Bump.sh.\n\nStep 3.5: Review and Validate\n\n  Objective: Ensure the accuracy and completeness of the OpenAPI document.\n  Action: Test each operation using your editor to ensure you have addressed any syntax errors across the entire document.\n\n\nAt this point, we have a complete and valid OpenAPI document. We can always go back and improve any summary, description, and example entries to make sure they are clear to the reader.\n\nKey Takeaways\n\nAs we wrap up this guide on creating an OpenAPI document for a TODO list API, it’s important to reflect on the iterative nature of API design and documentation. Starting with a basic structure and gradually enriching the details allows you to adapt to changes and insights gained throughout the development process.\n\n\n  \n    Start with Essentials: Begin with essential metadata in the info section and expand to operation details and security specifications. This approach keeps the initial setup manageable and scales complexity as you expand your documentation.\n  \n  \n    Iterative Refinement: With each phase of the documentation process, revisit and refine previous sections. This iterative refinement ensures that the document evolves alongside the API, maintaining accuracy and relevance.\n  \n  \n    Engage Stakeholders: Regularly review the API documentation with stakeholders, including developers, product managers, and potential API consumers. Their feedback is invaluable in shaping a practical and user-friendly API.\n  \n  \n    Utilize Examples: Including examples throughout your OpenAPI document is important for clear understanding. They provide guidance on how the API operates in real-world scenarios, making the documentation both instructive and actionable.\n  \n  \n    Security and Compliance: Don’t overlook the importance of accurately documenting security schemas and requirements. Clear documentation of authentication and authorization processes ensures that API consumers can securely interact with your API.\n  \n  \n    Documentation as a Living Document: Treat your OpenAPI documentation as a living document that grows and adapts. Changes in business requirements, user feedback, and technology should all trigger updates to ensure the documentation remains a reliable resource.\n  \n\n\nFinal Thoughts\n\nThe process of building and maintaining effective API documentation is continuous and dynamic. By adopting a phased approach, as demonstrated with our TODO list API, you create a strong foundation that accommodates growth and change. Remember, the ultimate goal is to provide clear, comprehensive, and usable documentation that serves both the creators and the consumers of the API.\n\nIncorporating these practices into your API development lifecycle will not only improve the quality of your API’s documentation but also its usability and longevity in the market."
        },
        {
          "id": "guides-bump-sh-tutorials-testing-with-microcks",
          "title": "Contract Testing APIs with Microcks",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Bump-sh tutorials",
          "tags": "",
          "url": "/guides/bump-sh-tutorials/testing-with-microcks/",
          "content": "After you’ve gone through planning and designing an excellent API, published your documentation to Bump.sh, got loads of useful feedback from stakeholders by mocking your API, and built (or generated) an API implementation, you’re pretty close to releasing the API into the wild.\n\nBefore you send this API out into the world, how do you know that the documentation and the API are actually… correct? Is the API doing something the documentation doesn’t mention? Is the documentation saying the API works differently to how it actually works?\n\nThere’s a lot of manual tedious error-prone stuff you could do, but why not use the same OpenAPI that powers your Bump.sh documentation to initiate a bunch of requests against the API, see if the requests are accepted as expected, and see if the responses from the API match the OpenAPI too?\n\nSetting that up yourself could be complicated, but Microcks is an excellent tool to help you do that within your existing Bump.sh-powered Git-based OpenAPI workflow.\n\nWhat is Microcks?\n\nMicrocks is a mocking and testing tool which helps you turn your OpenAPI into something that clients can experiment with and give you feedback which can be turned into changes quickly. You can update the OpenAPI, make some changes, deploy the updated mock, and continue iterating until people feel confident in the direction, but at some point you’ll want to start writing code and building your real API.\n\nEven if you’re not using the API design first workflow, if you have OpenAPI and an API, you need to make sure the two agree. This concept is known as contract testing. API developers have been scared of contract testing for decades because its a faff to set up, but when you’re using OpenAPI you already have a contract, so all you need to do is compare some real requests and responses to the API and see if they match the API, catching mistakes in both your API implementation and the OpenAPI which powers your API documentation, SDK, and everything else.\n\nStep 1: Set up Microcks\n\nFirst you’ll need to get Microcks set up, and that can be done with Docker, or it could be a hosted installation that lets your whole team/organization work with it. Setting Microcks up for testing is the same process as setting Microcks up for mocking, so follow that guide and come back here when you’re done.\n\n\n\nOnce Microcks knows about your API(s), we are almost ready to run compliance/contract testing. This means comparing the OpenAPI and the real API implementation, which of course means we’ll need to have the API running on a URL where Microcks can access it.\n\nStep 2: Make Local APIs Available for Testing (Optional)\n\nIf you already have your API deployed then you are well on your way, but when if your API is running on localhost then Microcks might have a rough time finding it. To fix this I used ngrok, a free and amazing tool which allows you to expose local HTTP servers to traffic anywhere via a HTTP tunnel.\n\nOnce ngrok is installed and you’ve created a free account, you can run your HTTP server and get ngrok to do it’s thing via a terminal command.\n\nFor example, a HTTP API running on port 8080 could do this:\n\n$ ngrok http http://localhost:8080/\n\n\nNgrok will output something like this in response:\n\nSession Status                online\nAccount                       Phil Sturgeon (Plan: Free)\nVersion                       3.14.0\nRegion                        Europe (eu)\nLatency                       36ms\nWeb Interface                 http://127.0.0.1:4040\nForwarding                    https://8eab-81-187-79-196.ngrok-free.app -&gt; http://localhost:8080/\n\n\nCopy that https://8eab-81-187-79-196.ngrok-free.app (which will change each time you run ngrok) and we can use that to point Microcks at where to test.\n\n\n  There’s a simpler alternative when your API runs on localhost. You just have to tell Microcks it is running on host.docker.internal. This is because Microcks is running into a Docker container and specific network - from its point of view localhost routes to itself. host.docker.internal is the alias of your host machine (ie. your laptop).\n\n\nOf course if you have your API deployed already then you can just take that URL which might be https://api-beta.example.com/.\n\nStep 3: Trigger a Test Run\n\nOn the Microcks dashboard click on the API you want to test, then click “+ New Test”. The New Test screen asks for a Test Endpoint, which will be the base of the service you want to test, where each OpenAPI path begins.\n\n\n\nYou also need to select a Runner, and Microcks has a few options here:\n\n\n  HTTP - A generic runner for any old HTTP API.\n  SOAP UI - Handy for working with old school SOAP APIs.\n  Postman - Work with Postman testing using whatever tests are defined in the collection if you imported one.\n  OpenAPI Schema - Use an OpenAPI description to handle contract testing a HTTP API.\n\n\nPick that last one, and hit “Launch test”. Microcks will now send a test request to every operation in your OpenAPI document, based on the data available in OpenAPI and any examples defined for parameters and request bodies.\n\nFor every operation that successfully ran, you’ll see a green tick, and others will show a red tick.\n\n\n\nClick “Full results” and expand each result to see what worked and what didn’t. For a success you will see a green tick, and the HTTP headers and the response body.\n\n\n\nFailure results can come in many forms, but could highlight failures like schema mismatches.\n\n\n\nMicrocks can even spot unexpected HTTP statuses.\n\n\n\nHere the contract testing has highlighted an important mistake: OpenAPI says that a 201 is supposed to be returned, but a 200 is being returned instead! Is the API wrong or is the OpenAPI wrong? You can now decide, and change one or the other, then run the tests again.\n\nIt’s also clear something is wrong with the response, because there is no data being returned, only the link, and the link is showing undefined instead of putting the UUID. Lots to fix here!\n\nDifferent Types of Failures\n\nMicrocks can discover lots of types of mismatch.\n\n\n  \n    The HTTP response body Microcks gets could contain properties which have different data to that defined in the schema.\n  \n  \n    The OpenAPI defines a property as required, but is missing in the response.\n  \n  \n    The OpenAPI defines a status code as being expected, but a different one is returned.\n  \n\n\nConformance Scores\n\nAll of these types of problem and more come together to provide a conformance index, and a conformance score.\n\n\n  The Conformance index is a kind of grade that estimates how your API contract (a.k.a API description) is actually covered by the samples you’ve attached to it. We compute this index based on the number of samples you’ve got on each operation, the complexity of dispatching rules of these operations and so on… It represents the maximum possible conformance score you may achieve if all your tests are successful.\n\n\n\n  The Conformance score is the current score that has been computed during your last test execution. We also added a trend computation if things are going better or worse comparing to your history of tests on this API.\n\n\nThis is especially useful if you have defined request and response pairs, by providing examples with matching names in OpenAPI for both requestBodies and responses.\n\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/BookingPayment'\n            examples:\n              Card:\n                summary: Card Payment\n                value:\n                  amount: 49.99\n                  currency: gbp\n                  source:\n                    object: card\n                    name: J. Doe\n                    number: '4242424242424242'\n                    cvc: 123\n\n      responses:\n        '200':\n          description: Payment successful\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/BookingPayment'\n              examples:\n                Card:\n                  summary: Card Payment\n                  value:\n                    id: 2e3b4f5a-6b7c-8d9e-0f1a-2b3c4d5e6f7a\n                    amount: 49.99\n                    currency: gbp\n                    source:\n                      object: card\n                      name: J. Doe\n                      number: '************4242'\n                      cvc: 123\n                    status: succeeded\n\n\nMicrocks knows that both the names Card are the same for a request and response, and so it can provide a more accurate score for whether those examples are accurate.\n\nIf they’re not accurate, then either the OpenAPI is wrong, or the API is wrong, and you can decide on the which one needs to be updated.\n\nAutomate Testing\n\nBelow is the standard GitHub Action used to deploy API changes to Bump.sh with one modification to also deploy changes to a Microcks server (which we assume was set up following instructions for mocking with Microcks).\n\n# .github/workflows/deploy-docs.yml\nname: Deploy API documentation\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  deploy-openapi:\n    if: ${{ github.event_name == 'push' }}\n    name: Deploy API documentation on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: 68ac0647-184a-4e9d-accc-682a5b1f7189\n          token: ${{secrets.BUMP_TOKEN}}\n          file: api/openapi.yaml\n\n      - uses: microcks/import-github-action@v1\n        with:\n          specificationFiles: 'api/openapi.yaml:true'\n          microcksURL: 'https://mocks.example.com/api/'\n          keycloakClientId:  ${{ secrets.MICROCKS_SERVICE_ACCOUNT }}\n          keycloakClientSecret:  ${{ secrets.MICROCKS_SERVICE_ACCOUNT_CREDENTIALS }}\n\n      # run the tests\n      - uses: microcks/test-github-action@v1\n        with:\n          apiNameAndVersion: 'Train Travel API:1.0.0'\n          testEndpoint: 'http://api-testing.example.com'\n          runner: OPEN_API_SCHEMA\n          microcksURL: 'https://mocks.example.com/api/'\n          keycloakClientId:  ${{ secrets.MICROCKS_SERVICE_ACCOUNT }}\n          keycloakClientSecret:  ${{ secrets.MICROCKS_SERVICE_ACCOUNT_CREDENTIALS }}\n          waitFor: '10sec'\n\n\nNow you have three steps in your workflow, that keep everything all up to date.\n\n\n  Deploy the OpenAPI to Bump.sh documentation.\n  Update the mock servers.\n  Run Microcks testing to make sure the API matches the expected contract.\n\n\nYou can play around with doing various combinations of these actions for complex workflows based on different branches and pull requests, and you can do it with any CI/CD setup like Jenkins, GitLab, Tekton, etc. Learn more about Microcks Automation to see how to use Microcks with other CI/CD systems, via the API, or using the CLI elsewhere. You can also use the Microcks Scheduler instead, to pull content from a repo on a regular schedule instead of pushing."
        },
        {
          "id": "guides-technical-writing-how-api-product-bundling-improves-the-developer-experience",
          "title": "How API Product Bundling Improves the Developer Experience",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Technical Writing",
          "tags": "",
          "url": "/guides/technical-writing/how-api-product-bundling-improves-the-developer-experience/",
          "content": "Developer experience (DX) is a critical element of any API product. A well-designed and intuitive API not only attracts developers but also empowers them to build innovative applications and integrations. In some cases, organizations may have a large number of APIs that address the needs of multiple audiences. This article explores how API product bundling, a strategic approach to packaging APIs, can significantly enhance DX.\n\nBackground: The API-Related Challenges Encountered at a Global Hotel Chain\n\nSeveral years ago, I collaborated with a world-wide hotel chain embarking on growing their new API platform. This initiative was aimed at enabling seamless integration with partners and third-party developers, thereby fostering a broader ecosystem around their services.\n\nNot long after the API platform started to grow from internal developers to partners, we encountered a challenge. How do we offer a subset of our APIs to specific partners? For example, Online Travel Agencies (OTAs) need to view room availability with their pricing along with booking rooms sold on their website. However, they should not have access to loyalty program details or other internal operation details. Loyalty partners may wish to look up the points for a program member to exchange them for other rewards on their website, but they shouldn’t be able to book reservations. Of course, the hotel web and mobile apps need access to nearly all API capabilities.\n\nThis is where we leveraged API product bundling to help us limit the availability of API documentation presented to each of our partners, based on what they were allowed to use.\n\nThe Power of Bundling with Purpose\n\nAPI product bundling, when implemented strategically, offers a powerful solution to address these challenges. The core concept lies in packaging a collection of APIs into curated bundles that cater to specific developer needs. This approach offers several advantages:\n\nImproved Developer Discovery: By creating well-defined bundles with clear use cases, developers can easily identify the API operations most relevant to their project. This streamlines the discovery process and reduces the time spent deciphering individual API functionalities.\n\nEnhanced Developer Onboarding: Bundled APIs can be accompanied by tailored documentation, code samples, and tutorials specifically designed for the intended developer segment. This facilitates a smooth onboarding experience, allowing developers to start building integrations quickly.\n\nLimiting API Scope: Developers are provided only with the API documentation that they will be able to use. This avoids those uncomfortable experiences where a developer starts to use an API in a development environment, only to find out they won’t be allowed to use it in production.\n\nAPI Bundling for the Global Hotel Chain\n\nAPI bundling initially started with two bundles: one for internal developers, along with a dedicated bundle that was created specifically for OTA partners. This kept things manageable initially, as API reference documentation was separated into two separate bundles and offered as separate developer portals.\n\nThe OTA bundle might grant access to functionalities related to room availability, pricing, and reservation creation, while restricting access to operations that could potentially impact brand image or pricing strategies – for example, modifying existing reservations or managing loyalty programs. This ensures that OTAs have the necessary tools to sell rooms effectively without compromising the hotel chain’s control over its offerings.\n\nThrough the use of some fancy automation scripts, we generated two sets of reference documentation, one for each bundle, that contains the specific API operations they were allowed to use. The link to the OTA reference documentation was shared with their partners, while the internal developer portal was offered for internal app development.\n\nOver time, we realized that we would need to add three more bundles to address various partners in the marketplace. While we were able to keep the bundle count stable at five, the complexity only increased when generating API documentation for these additional bundles. Add in getting started guides and developer cookbooks, and we had some complex documentation management efforts underway. This led us to generate separate developer portals for each bundle.\n\nBeyond Reference Documentation: The Power of Developer Portals\n\nWhile well-structured reference documentation is crucial for a positive DX, API product bundling can be further enhanced by leveraging dedicated developer portals. These portals act as a central hub for developers to discover, explore, and integrate with APIs available in their specific bundle. In the context of my experience with the hotel chain, a developer portal with the following functionalities significantly improved DX:\n\nBundle Showcase: The portal could prominently feature their API product bundle, clearly outlining the API capabilities  associated with their specific area of focus. No confusion about which APIs they should use or which APIs they would never be allowed to integrate.\n\nInteractive Documentation: Interactive documentation within the portal could allow developers to explore their specific set of API operations within a bundle and test them in a sandbox environment. This would significantly reduce the learning curve and expedite integration.\n\nTailored Getting Started Guides: The portal leveraged the focused nature of each bundle by offering tailored getting started guides that addressed their common use cases. In addition, each product bundle was able to leverage specific terminology based on their partner perspective. This helped to accelerate integration by offering specific examples that addressed their needs, rather than generic solutions that didn’t resonate with their understanding and problem space.\n\nThe result was five dedicated API developer portals that had a level of automation to generate them. Their partners found that the experience was smooth and much faster to get up-to-speed on their specific APIs. Plus, executives within the global hotel chain were so excited that they shared the portals with anyone that would listen (and some that didn’t).\n\nHowever, what came with it was the added complexity of technical writing to accommodate these varying perspectives and a complex build process that had to be maintained by a single developer. The cost of maintaining the separate portals was higher than they had expected.\n\nLeveraging Existing Solutions: Cost Savings and Efficiency\n\nOne additional aspect worth mentioning is the potential cost savings associated with leveraging a platform like Bump.sh, which offers multiple API consumer hubs for managing and scaling API integrations. Utilizing such a platform could have potentially saved the hotel chain over $150k in internal development efforts per year required to maintain their internal portal generation code by streamlining the management of developer portals using API consumer hubs tailored for each need.\n\nRecommendations for Implementing API Product Bundles\n\nFor organizations that wish to create their own API product bundles, several actions can be taken to ensure a streamlined, secure, and user-friendly experience. Here are key steps to consider:\n\n\n  \n    Establish API Hubs and Categorize Documentation\n\n    Creating centralized API hubs is essential for managing multiple API product bundles effectively. These hubs serve as the entry point for developers and partners, offering a comprehensive overview of available APIs.\n\n    In some cases, you may have documentation that belongs in one or more portals. In this case, consider assigning topics to your documentation that can then be used to publish related documentation to the proper portals.\n  \n  \n    Implement User Access Management\n\n    Managing user access at both the portal and document levels is crucial for maintaining security and ensuring that developers only see the APIs they are authorized to use.\n\n    \n      Per Portal Management: Set up different portals or access points for various audiences, such as internal developers, partners, or third-party developers. Each portal should display only the relevant API bundles and documentation.\n      Per Doc Management: Within these portals, control access at the individual document level. This ensures that sensitive or advanced API functionalities are only accessible to authorized users. FYI, this would have helped us reduce the number of API portals that we required.\n    \n  \n  \n    Utilize OpenAPI Specification Overlays\n\n    OpenAPI Specification (OAS) overlays are a powerful tool for extending and modifying API definitions without changing the underlying OAS document. They enable more flexible and dynamic documentation and API management.\n\n    Use OAS overlays to customize the documentation view for different audiences. This can include adding additional descriptions, usage examples, or constraints that are specific to a particular bundle or user segment.\n  \n  \n    Support for APIs.json\n\n    APIs.json is a discovery format for APIs that makes it easier for developers to find and understand available APIs. Supporting APIs.json can enhance the visibility and usability of your API bundles.\n\n    Create an APIs.json file that lists all your API bundles and includes metadata such as titles, descriptions, and links to documentation. This file should be kept up-to-date and easily accessible to ensure developers can quickly discover the APIs they need.\n\n    Combining these actions forms a holistic approach to creating and managing API bundles:\n\n    \n      Centralized Hubs: Establish API hubs as the go-to resource for developers, with well-organized documentation and clear navigation.\n      Access Management: Implement robust user access controls to safeguard sensitive information and ensure developers only see what they need.\n      Dynamic Documentation: Leverage OAS overlays to keep documentation dynamic and relevant, accommodating different user needs and API versions.\n      Discovery Format: Utilize APIs.json to enhance discoverability and streamline access to your API offerings.\n    \n  \n\n\nConclusion\n\nAPI product bundling, when implemented thoughtfully, serves as a powerful tool to enhance developer experience. By offering curated bundles with clear use cases, granular access control, and leveraging developer portals, organizations can empower developers to build innovative integrations that unlock the true potential of their APIs."
        },
        {
          "id": "guides-bump-sh-tutorials-mocking-with-microcks",
          "title": "Mocking APIs with Microcks",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Bump-sh tutorials",
          "tags": "",
          "url": "/guides/bump-sh-tutorials/mocking-with-microcks/",
          "content": "You’ve created beautiful documentation for your API with Bump.sh, powered by OpenAPI, what’s next? If you’ve been following the API design-first workflow the next phase is gathering feedback on the proposed design before investing loads of time building it.\n\nSharing the documentation is a good start, but you can get even better feedback by giving stakeholders a mock server to interact with. A mock server simulates an API, allowing stakeholders to see if all the data they need is available, and give feedback on how easily their workflows can be solved based on the endpoints in the API. This can be thought of like a study group, something user researchers are used to doing for frontends, but is just as valuable for APIs.\n\nThis used to be done with “prototypes” or manually made mock servers, but seeing as Bump.sh users have already got the API described entirely by OpenAPI already, setting up mock servers is a whole lot easier using OpenAPI-powered tools like Microcks to do the hard work for you. Using OpenAPI instead of code means changes can be made to the mock server far quicker than getting the API developments to change loads of code.\n\nLet’s take a look at using Microcks specifically, and see how we can tie it into your existing Bump.sh documentation.\n\nStep 1: Set up Microcks locally\n\nMicrocks is a Java (Spring Boot) application, which comes packaged as a Docker container, so it can be deployed anywhere either of those environments are happy. Setting it up locally is probably the first step to see how it works.\n\nWhether you use Docker or prefer Podman, grab one of those applications to install Microcks within a container.\n\nAssuming you’ve got Docker installed and running, you can use following command to get Microcks running on your computer.\n\ndocker run -p 8585:8080 -it --rm quay.io/microcks/microcks-uber:latest-native\n\n\nWhen that’s done, open a browser tab and point to the http://localhost:8585 endpoint, changing the port if you picked a different one.\n\n\n\nStep 2: Add Your First API\n\nThere are several ways to get OpenAPI into Microcks, but for the sake of simplicity we’re going to use the web interface to upload your OpenAPI. If you are just starting out and don’t have any OpenAPI yet, why not use the Train Travel API for now.\n\nOnce we’ve got Microcks loaded in the browser, click on “Importers”, and click the “Upload” button.\n\n\n\nThe modal that pops up is asking for an Artifact, which is referring to various documents that could describe an API, like a Postman Collection and other API description documents. We can pop our OpenAPI in there, which is the same openapi.yaml document that you deploy to Bump.sh.\n\nThe question about primary or secondary artifacts can be ignored for now.\n\n\n\nOnce you’ve uploaded, go to “APIs / Services” and click on your new API, which should have the same name as whatever was in the info.title of your OpenAPI. Below it you’ll see a bunch of operations, which are all the endpoints that Microcks found in your OpenAPI.\n\nStep 3: Try out the Mock Endpoints\n\nOpen up one of the operations and see what Microcks thinks about it.\n\n\n\nYou should see a URL which you can copy, and the sample JSON it has pulled from the OpenAPI examples object.\n\nhttp://localhost:8585/rest/Train+Travel+API/1.0.0/bookings\n\n\nIt’s basically:\n\n&lt;wherever microcks is running&gt;/rest/&lt;info.name&gt;/&lt;info.version&gt;/&lt;operation path&gt;\n\n\nIf that operation has a GET method defined you can copy a curl command that’ll look something like this:\n\ncurl -X GET 'http://localhost:8585/rest/Train+Travel+API/1.0.0/bookings' -H 'Accept: application/json'\n\n\nRunning this command will then return the sample Microcks was showing the web interface.\n\n{\n  \"data\": [\n    {\n      \"id\": \"bfc5af2c-f477-43c4-8bdf-a00bdb939d65\",\n      \"trip_id\": \"efdbb9d1-02c2-4bc3-afb7-6788d8782b1e\",\n      \"passenger_name\": \"John Doe\",\n      \"has_bicycle\": true,\n      \"has_dog\": true\n    },\n    {\n      \"id\": \"b2e783e1-c824-4d63-b37a-d8d698862f1d\",\n      \"trip_id\": \"b2e783e1-c824-4d63-b37a-d8d698862f1d\",\n      \"passenger_name\": \"Jane Smith\",\n      \"has_bicycle\": false,\n      \"has_dog\": false\n    }\n  ],\n  \"links\": {\n    \"self\": \"https://api.example.com/bookings\",\n    \"next\": \"https://api.example.com/bookings?page=2\"\n  }\n}\n\n\nGetting a collection is a handy first example, but next lets try working with resources.\n\nMicrocks uses examples with a particular naming convention, pairing up request examples and response examples with the same name. As a best practice, the naming convention could &lt;resource&gt;_&lt;id&gt;, so sticking with the Train Travel API, we could do this:\n\n /bookings/{bookingId}:\n    get:\n      summary: Get a booking\n      operationId: get-booking\n      parameters:\n        - name: bookingId\n          in: path\n          required: true\n          description: The ID of the booking to retrieve.\n          schema:\n            type: string\n            format: uuid\n          examples: \n            booking_1725ff48-ab45-4bb5-9d02-88745177dedb:\n              value: 1725ff48-ab45-4bb5-9d02-88745177dedb\n            booking_bfc5af2c-f477-43c4-8bdf-a00bdb939d65:\n              value: bfc5af2c-f477-43c4-8bdf-a00bdb939d65\n      responses:\n        '200':\n          description: The booking details\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Booking'\n              examples:\n                booking_1725ff48-ab45-4bb5-9d02-88745177dedb:\n                  summary: John Doe\n                  value:\n                    id: 1725ff48-ab45-4bb5-9d02-88745177dedb\n                    trip_id: efdbb9d1-02c2-4bc3-afb7-6788d8782b1e\n                    passenger_name: John Doe\n                    has_bicycle: true\n                    has_dog: true\n                booking_bfc5af2c-f477-43c4-8bdf-a00bdb939d65:\n                  summary: Billy Bikeless\n                  value:\n                    id: bfc5af2c-f477-43c4-8bdf-a00bdb939d65\n                    trip_id: efdbb9d1-02c2-4bc3-afb7-6788d8782b1e\n                    passenger_name: Billy Bikeless\n                    has_bicycle: false\n                    has_dog: true\n\n\nUpload that to Microcks again and it will now be aware of these two examples, and show you the mock URLs for both.\n\n\n\nNow you can call either of them, and get different HTTP responses with the appropriate example data being used for each.\n\ncurl -X GET 'http://localhost:8585/rest/Train+Travel+API/1.0.0/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb' -H 'Accept: application/json'\n\ncurl -X GET 'http://localhost:8585/rest/Train+Travel+API/1.0.0/bookings/bfc5af2c-f477-43c4-8bdf-a00bdb939d65' -H 'Accept: application/json'\n\n\nThis is already really useful, you could use this to build a read-only API client on your computer without needing the actual API to actually exist.\n\nIf you need to send POST, PUT or PATCH requests you can do that too. Let’s try sending a POST request. The “Copy to clipboard” functionality in Microcks won’t automatically give you a predefined request body, but pop back over to your Bump.sh API docs to get a better curl example and it should look a bit like this.\n\ncurl -X POST 'http://localhost:8585/rest/Train+Travel+API/1.0.0/bookings' \\\n  -H 'Accept: application/json' \\\n  -H 'Content-type: application/json' \\\n  -d '{\"passenger_name\":\"New Passenger\",\"has_bicycle\":false,\"has_dog\":false,\"trip_id\":\"4f4e4e1-c824-4d63-b37a-d8d698862f1d\"}'\n\n\n{\n  \"id\": \"efdbb9d1-02c2-4bc3-afb7-6788d8782b1e\",\n  \"trip_id\": \"efdbb9d1-02c2-4bc3-afb7-6788d8782b1e\",\n  \"passenger_name\": \"John Doe\",\n  \"has_bicycle\": true,\n  \"has_dog\": true,\n  \"links\": {\n    \"self\": \"https://api.example.com/bookings/efdbb9d1-02c2-4bc3-afb7-6788d8782b1e\"\n  }\n}\n\n\nThat’s somewhat handy, but it’s showing me a generic response instead of the data sent in the POST request. Named examples would not help here, as we want to use client data from the request body and not just show predetermined examples. How can we have New Passenger instead of John Doe, and how can we see the actual trip_id instead of the default one?\n\nStep 4: Customizing Responses\n\nMocks can be made more powerful with dynamic mocking, which is made possible through Microcks’ templating system. Using a combination of predefined variables, built-in functions, and some JSON Pointers, we can provide a dynamic example in OpenAPI which put together a more useful response.\n\nresponses:\n  '201':\n    description: Booking successful\n    content:\n      application/json:\n        schema:\n          # snip\n        examples:\n          new_booking:\n            summary: New Booking\n            value: |-\n              {\n                \"id\": \"{{ uuid() }}\",\n                \"trip_id\": \"{{ request.body/trip_id }}\",\n                \"passenger_name\": \"{{ request.body/passenger_name }}\",\n                \"has_bicycle\": {{ request.body/has_bicycle }},\n                \"has_dog\": {{ request.body/has_dog }},\n                \"links\": {\n                  \"self\": \"https://api.example.com/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb\"\n                }\n              }\n\n\nGo back to Importers, Upload the openapi.yaml document again, and it will merge changes into API so we can see how they look.\n\n**curl** -X POST 'http://localhost:8585/rest/Train+Travel+API/1.0.0/bookings' \\\n  -H 'Accept: application/json' \\\n  -H 'Content-type: application/json' \\\n  -d '{\"passenger_name\":\"New Passenger\",\"has_bicycle\":false,\"has_dog\":false,\"trip_id\":\"4f4e4e1-c824-4d63-b37a-d8d698862f1d\"}'\n\n\nNow we will see the values we sent showing back up, and the computed random UUID.\n\n{\n  \"id\": \"a248a638-000a-44b4-b19b-0ca30507a940\",\n  \"trip_id\": \"4f4e4e1-c824-4d63-b37a-d8d698862f1d\",\n  \"passenger_name\": \"New Passenger\",\n  \"has_bicycle\": false,\n  \"has_dog\": false,\n  \"links\": {\n    \"self\": \"https://api.example.com/bookings/1725ff48-ab45-4bb5-9d02-88745177dedb\"\n  }\n}\n\n\nFinally, that HATEOAS link at the bottom there is not right. The id is a248a638-000a-44b4-b19b-0ca30507a940, so how can we get that showing up in the link? If we used {{ uuid() }} again it would be generate a second different UUID, Microcks templating has a brilliant feature called capture.\n\nexamples:\n  new_booking:\n    summary: New Booking\n    value: |-\n      {\n        \"id\": \"{{ uuid() &gt; put(bookingId) }}\",\n        \"trip_id\": \"{{ request.body/trip_id }}\",\n        \"passenger_name\": \"{{ request.body/passenger_name }}\",\n        \"has_bicycle\": {{ request.body/has_bicycle }},\n        \"has_dog\": {{ request.body/has_dog }},\n        \"links\": {\n          \"self\": \"https://api.example.com/bookings/{{ bookingId }}\"\n        }\n      }\n\n\nUpload that again. Run the command again.\n\n{\n  \"id\": \"b4441c3d-b659-4f57-95d3-ce6ee592da7c\",\n  \"trip_id\": \"4f4e4e1-c824-4d63-b37a-d8d698862f1d\",\n  \"passenger_name\": \"New Passenger\",\n  \"has_bicycle\": false,\n  \"has_dog\": false,\n  \"links\": {\n    \"self\": \"https://api.example.com/bookings/b4441c3d-b659-4f57-95d3-ce6ee592da7c\"\n  }\n}\n\n\nPerfect! Now you can do almost anything you need to do with the API.\n\nStep 5: Deploy Microcks Somewhere\n\nWorking with Microcks locally was an OK way to get the hang of it, and could be useful for some solo developers, but the chances are you’ll want to get this off your laptop and share it with people.\n\nYou can deploy Microcks anywhere that takes Docker/Kubernetes instances, and they have lots of documentation on your deployment options for Microcks.\n\nOne approach is to deploy the Docker container into a managed Kubernetes cluster on Google Kubernetes Engine, which has free trials and free tiers to ease you into the process.\n\n\n\nThis puts Microcks behind a publicly accessible load balancer, so you can stick some DNS on it and have everyone able to call the hosted version of the mock server.\n\ncurl -X GET 'https://mocks.example.com/rest/Train+Travel+API/1.0.0/bookings'\n\n\nOnce this is done we can replace the manual uploads with some automatic solutions too.\n\nStep 6: Automate Mock Updates\n\nMost Bump.sh users use some form of Continuous Integration (CircleCI, GitHub Actions, Jenkins, etc.) to push changes to their API documentation whenever the source code is changed, and you can work this way with Microcks, or there are some alternatives you can try out.\n\nUpdate Mocks with Continuous Integration\n\nBelow is the standard GitHub Action used to deploy API changes to Bump.sh with one modification to also deploy changes to your Microcks server.\n\n# .github/workflows/deploy-docs.yml\nname: Deploy API documentation\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  deploy-openapi:\n    if: ${{ github.event_name == 'push' }}\n    name: Deploy API documentation on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;your-doc-id-or-slug&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: api/openapi.yaml\n\n      - uses: microcks/import-github-action@v1\n        with:\n          specificationFiles: 'api/openapi.yaml:true'\n          microcksURL: 'https://mocks.example.com/api/'\n          keycloakClientId:  ${{ secrets.MICROCKS_SERVICE_ACCOUNT }}\n          keycloakClientSecret:  ${{ secrets.MICROCKS_SERVICE_ACCOUNT_CREDENTIALS }}\n\n\nYou’ll need to set up some secrets on your repository for that Microcks service account, but then you’re done! A fully functioning mock server running on the cloud, which you can interact with internally or externally depending on how you set it up.\n\n\n  Learn more about Microcks Automation to see how to push updates to Microcks using other CI systems, via the API, or using the CLI elsewhere. You can also use the Microcks Scheduler instead, to pull content from a repo on a regular schedule instead of pushing.\n\n\nStep 7: Add Mock Server to API Documentation\n\nOnce the mock server is up and running, you can help make it easier to find by adding it to your Bump.sh API documentation. To do this, we can add the server URL into the servers list like this:\n\nservers:\n  - url: https://api.example.com\n    description: Production\n  \n  - url: https://mocks.example.com/rest\n    description: Mock Server\n\n\nAdding this second server URL will offer users a dropdown menu in the Bump.sh documentation.\n\n\n\nThe mock server is now an option, and all of the URLs and example HTTP requests will show up using the chosen server URL.\n\n\n\nIf there’s no production API only the mock server is ready then only define that:\n\nservers:\n  - url: https://mocks.example.com/rest\n    description: Mock Server\n\n\n\n  Perhaps you cannot update the OpenAPI document to get that server added as it’s generated by somebody else or hosted online, in which case take a look at our Overlays guide."
        },
        {
          "id": "guides-technical-writing-how-our-api-docs-can-ease-the-pain-of-api-deprecation",
          "title": "How Our API Docs Can Ease the Pain of API Deprecation",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Technical Writing",
          "tags": "",
          "url": "/guides/technical-writing/how-our-api-docs-can-ease-the-pain-of-api-deprecation/",
          "content": "APIs are not static, as new operations are added and existing operations are improved as API usage grows. As customer and business needs evolve, APIs need to adapt as well. This often leads to the inevitable process of API deprecation – the gradual phasing out of an old API in favor of a newer, improved version.\nWhile deprecation is a necessary step in the API lifecycle, it can be a frustrating and disruptive experience for API consumers if not handled properly. In this article, we will discuss the often-overlooked deprecation stage and explore how well-crafted API documentation can significantly ease the pain for both API providers and consumers.\n\nWhy Deprecate APIs?\n\nSeveral factors can necessitate API deprecation:\n\nSecurity Vulnerabilities: Security is paramount. If vulnerabilities are discovered in an existing API, it may be safer to deprecate it and develop a new one with stronger security measures.\n\nTechnical Limitations: APIs built with older technologies might not be able to handle the demands of modern applications. Deprecation allows for the development of a new API with a more robust architecture.\n\nFeature Obsolescence: As functionalities evolve, some features in an existing API might become irrelevant. Deprecation allows for the removal of unused code, streamlining the API.\n\nImproved Design: Sometimes, APIs are simply not well-designed. Deprecation paves the way for a new API with a clearer and more intuitive design.\n\nThe Role of API Documentation in Deprecation\n\nWhile deprecation is a strategic decision, its execution hinges on effective product management and communication. This is where API documentation can help us out. Here’s how well-written API documentation can improve communication throughout the deprecation process:\n\nAnnouncing the Deprecation: Clearly state the upcoming deprecation of the API and the timeline for its removal. This gives API consumers ample time to plan and migrate to the new API.\n\nExplaining Your Reasons: Don’t leave API consumers guessing. Explain the rationale behind the deprecation – is it a security issue, a technical limitation, or a design improvement?\n\nIntroducing the New API: Detailed documentation for the new API should be readily available alongside the deprecation notice. This includes API endpoints, authentication methods, request and response formats, and code examples. If you are announcing the deprecation prior to the new API being ready for release, consider releasing details in stages as the new API’s features begin to stabilize.\n\nMapping Old to New: Highlight the changes made between the old and new APIs. This includes a mapping between the deprecated functionalities and their replacements in the new API. This mapping helps developers understand how to update their code using the new API.\n\nVersioning: Implement API versioning to support both the old and new APIs concurrently for a defined period. This allows developers to test and migrate their code at their own pace.\n\nDeprecation Warnings: Include clear warnings within the API documentation about deprecated features and functionalities. Warn developers about the upcoming removal and the recommended replacement within the new API.\n\nOpenAPI Deprecation Flags: The OpenAPI Specification’s deprecation boolean flag can be applied to each or all operations, helping to generate warnings from within code generated helper libraries and SDKs.\n\nSample Code Migration: Provide sample code snippets demonstrating the migration process from the old API to the new one. This can significantly reduce development time and effort required for API consumers.\n\nSupport Resources: Offer additional support resources, such as migration guides, FAQs, and a dedicated communication channel for API consumers during the deprecation period. This demonstrates a commitment to helping developers through the transition.\n\nUse the HTTP Sunset Header: RFC 8594 provides a machine-readable method of noting the deprecation of an API by including the sunset date. API monitoring solutions are beginning to support this header as a way to detect, log, and notify an API’s sunset date for those that might not notice it via other communication methods.\n\nSince many of these changes involve documentation updates, you may want to consider applying a branching strategy to help you perform updates to all of your content and roll it out at once when ready. Also consider taking advantage of automatic or manual release processes to manage the rollout of your announcement when you are ready.\n\nBest Practices for Deprecation Communication\n\nBeyond the documentation, clear communication practices enhance the deprecation process:\n\nEarly Notification: Announce the deprecation well in advance, ideally several months before the final removal.\n\nMultiple Channels: Utilize emails, blog posts, community forums, and developer portals to spread the message.\n\nProactive Support: Be proactive in offering support to API consumers through dedicated communication channels. Answer their questions and address their concerns about the migration process.\n\nProvide a Grace Period: Set a reasonable timeframe for API removal, allowing developers ample time for migration.\n\nBenefits of a Smooth Deprecation\n\nBy crafting clear, comprehensive API documentation and implementing best practices for communication, API providers can achieve a smooth deprecation with minimal disruption to their API consumers. They also ensure that their developers are well-prepared and have sufficient time to migrate their applications to the new API seamlessly, minimizing downtime. Finally, a well-managed deprecation process fosters trust and goodwill between the API provider and its developer community.\n\nConclusion\nAPI deprecation is an inevitable part of the API lifecycle. By understanding the importance of clear communication and utilizing the power of well-crafted API documentation, you can transform a potentially disruptive experience into a smooth transition for your API consumers. Remember, a well-managed deprecation not only minimizes disruption but also opens the door for further innovation and a stronger relationship with your developer community."
        },
        {
          "id": "guides-bump-sh-tutorials-migrate-from-swaggerhub",
          "title": "Migrate from SwaggerHub",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Bump-sh tutorials",
          "tags": "",
          "url": "/guides/bump-sh-tutorials/migrate-from-swaggerhub/",
          "content": "Tools like SwaggerHub provide an editing and hosting experience for OpenAPI documents, allowing teams to fork APIs, add new versions, diff, and compare, which was pretty handy back in the day. These days it can feel like recreating Git with a walled-garden, needing to be imported and exported, or joined together with “integrations” that involve manually setting up webhooks to ping your other tools when there are changes. Many teams are starting to question how useful that is.\n\nBump.sh gives teams more flexibility in how they work, but the most common workflow assumes that your OpenAPI is your source of truth, and that source of truth lives with the source code in Git, so instead of managing source code and OpenAPI separately you have it all in the same pull requests as you go. Your documentation can still be published immediately on changes, but it all happens from your Git repository or CI/CD setup as a central point, instead of being stuck in somebody elses server.\n\nStep 1: Export OpenAPI from SwaggerHub\n\nTo liberate your OpenAPI from SwaggerHub for good, head to “My Hub”, open each API one at a time, then click “Export” &gt; “Download API” &gt; “YAML Unresolved”.\n\n\n\nThis will download a version which still has $ref’s, which will keep the file size smaller and avoid duplication of components.\n\nNow pop the downloaded OpenAPI into your source code repository, and give it a tidy name in the process.\n\nmv ~/Downloads/ME_10-train-travel_api-1.0.0-swagger.yaml ~/src/train-travel-api/openapi.yaml\n\n\nNow you can add this OpenAPI to your source control, which assuming Git is a case of:\n\ncd ~/src/train-travel-api\n\ngit add openapi.yaml\n\ngit commit -m \"docs: imported openapi\"\n\n\nStep 2: Setting up an OpenAPI Editor\n\nOne thing that SwaggerHub does well is providing an OpenAPI editing experience. Users write YAML/JSON in an editor window on the left, and see a preview of that OpenAPI as documentation on the right. This gives new users of OpenAPI more confidence, because they can see their changes as they work on it.\n\nUsing open-source tooling can provide this exact same experience, easier, cheaper, and more powerful, without needing to pay for a hosted service. Whatever your favorite IDE or text-editor, there’s an OpenAPI extension you can use to bring in that preview experience on your development machine.\n\nVS Code OpenAPI Extension\n\nVS Code users have a variety of popular extensions to chose from.\n\n\n  OpenAPI Editor by 42Crunch - code navigation, linting, SwaggerUI or ReDoc preview, IntelliSense, schema enforcement and generation, schema definition links, snippets as well as static and dynamic security analysis.\n  OpenAPI Preview by Andreas Zoellner - Preview panel using Stoplight Elements.\n  OpenAPI Viewer by Andrew Butson - Preview panel using RapiDoc.\n\n\nThe OpenAPI Editor from 42 Crunch is a powerful choice, offering not just a preview panel but auto-complete and a “explorer” view to help you move around the OpenAPI document. It also has a choice of preview tools, with Swagger UI being the exact same tool used on SwaggerHub.\n\n\n\nThe other tools offer just a preview experience, but offer two alternative open-source OpenAPI rendering tools to give you a few options in how you look at the OpenAPI as you go.\n\nJetBrains IntelliJ\n\nThe JetBrains team clearly recognize the importance of OpenAPI to their IntelliJ, PhpStorm, RubyMine, etc. community because they’ve created an official extension: Jetbrains OpenAPI Specifications.\n\n\n\nThe 42Crunch team have also built an extension: OpenAPI​ Editor will provide IntelliSense, “Go to Definition” for $ref, and the usual preview showing SwaggerUI or ReDoc.\n\nOther Editors\n\nCheck on OpenAPI.Tools to find more editors with OpenAPI preview functionality like Eclipse and Vim.\n\nIf there are no OpenAPI preview tools for your editor of choice, you can open up a browser window and use the Bump.sh CLI’s preview --live command to see how changes look as you work on your document.\n\n$ bump preview openapi.yaml --live\n\n* Let's render a preview on Bump... done\n* Your preview is visible at: https://bump.sh/preview/4a478f7d-8776-4c43-bc01-cf58dd7dd5f5 (Expires at 2024-05-29T16:17:06+02:00)\nWaiting for changes on file openapi.yaml......\n\n\nStep 3: Publish OpenAPI as Documentation\n\nInstead of having to click around a web interface to fork versions, “commit” changes in a web interface, make sure the changes are published, etc. Bump.sh allows you to automate the changes.\n\nYou can set up your documentation to deploy using the command-line, with the Bump.sh GitHub Action, or using any other Continuous Integration setup you like. There’s even an API for more exciting deployment integration opportunities.\n\nA common workflow is to have Bump.sh make previews on Git branches, then deploy the main version of the documentation when a branch is merged. This allows you to defer all configuration about how reviews work, what checks should be involved, who is in charge of approving changes, etc to the pull request settings of your source control.\n\n\n\nOnce a guide is published you can view the hosted documentation on Bump.sh, and it will look a little something like this: Train Travel API Documentation.\n\nOnce you’ve done this for all of your APIs and got them publishing on commit, you can close down your SwaggerHub account and update links to your API documentation.\n\nStep 4: Use Hubs to Group Your APIs (Optional)\n\nIf you had your APIs grouped into different organizations you can either make multiple Bump.sh organizations, or you can use Hubs to group APIs together."
        },
        {
          "id": "guides-technical-writing-git-workflows-for-api-technical-writers",
          "title": "Git Workflows for API Technical Writers",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Technical Writing",
          "tags": "",
          "url": "/guides/technical-writing/git-workflows-for-api-technical-writers/",
          "content": "Technical writers are often challenged with keeping API documentation in sync with an ever-evolving API design. This article explores the challenges and solutions for tackling this ongoing struggle, focusing on parallel development and leveraging automation to streamline the process.\n\nBenefits of Utilizing Git Workflows for API Documentation\n\nIncorporating git into your technical writing workflow offers several advantages:\n\nVersion control: Git workflows ensure that all documentation changes are tracked and versioned, enabling easy rollback and historical analysis.\n\nCollaboration: Git facilitates collaboration between technical writers and the development team, allowing for ongoing feedback and improvement.\n\nAutomation: Integration with CI/CD pipelines allows for automated documentation updates whenever the API codebase changes.\n\nHowever, there isn’t a one-size-fits-all approach. Let’s explore common scenarios that technical writers will face and some workflow options for making the documentation process as smooth as possible.\n\nThe Challenges of Technical Writing with Code-Generated Docs\n\nCode annotations are a common method for generating OpenAPI specification documents directly from source code. Below is an example provided from the baeldung website on how SpringDoc, a Java-based Spring Boot library, helps produce OpenAPI Specification documents from Java code:\n\n@Operation(summary = \"Get a book by its id\")\n@ApiResponses(value = { \n  @ApiResponse(responseCode = \"200\", description = \"Found the book\", \n    content = { @Content(mediaType = \"application/json\", \n      schema = @Schema(implementation = Book.class)) }),\n  @ApiResponse(responseCode = \"400\", description = \"Invalid id supplied\", \n    content = @Content), \n  @ApiResponse(responseCode = \"404\", description = \"Book not found\", \n    content = @Content) })\n@GetMapping(\"/{id}\")\npublic Book findById(@Parameter(description = \"id of book to be searched\") \n  @PathVariable long id) {\n    return repository.findById(id).orElseThrow(() -&gt; new BookNotFoundException());\n}\n\n\nWhile this approach ensures that documentation is closely aligned with the codebase, it often results in documentation that is too technical, lacks an external consumer-based context, or is simply incomplete due to limited time.\n\nAdditionally, technical writers are now forced to edit documentation from within the source code to make improvements. As you can imagine, editing source code with these annotations, while trying to focus on producing clear copy that improves the developer experience, is challenging at best.\n\nIf you are a technical writer working with a team generating OpenAPI-based documentation from source code, consider the following ideas to improve your workflow:\n\nMerge improved copy into the source: Tech writers produce improved documentation to an engineer on the team that will merge it into the source code on behalf of the technical writer. While this is less efficient, it can help technical writers to improve the quality of documentation without changing existing development processes. For technical writers familiar with git and code, they may opt to perform the work themselves using a combination of feature branches and an existing CI/CD pipeline to verify that their changes didn’t break any code.\n\nUse code annotations for one-time generation: Rather than using code annotations as the only API documentation method, consider generating the OpenAPI Specification once, then adopting the OpenAPI Specification document as the primary source of truth. This avoids the need for technical writers to edit source code, while reducing the need to author a YAML-based OpenAPI document from scratch. In this scenario, the OpenAPI document may be kept in the same or separate repository.\n\nOpenAPI specification overlays: The use of OpenAPI overlays allows the technical writer to replace a portion of an existing OpenAPI document with new content. This allows technical writers to enrich the documentation without cluttering the codebase while operating independently. Overlay support varies by vendor, but is supported by Bump.sh.\n\nFor Greenfield API Design, Collaboration is Key\n\nBy using a design-first approach for new (greenfield) APIs,, the development team prioritizes API design before code. This is accomplished by creating the OpenAPI document before implementing the API code. The OpenAPI document becomes the source of truth for how the API is to be implemented and is kept up-to-date and design decisions are made.\n\nUnfortunately, technical writers may be often left out of the design process. Involving technical writers in the API design process has several advantages:\n\n\n  Design Input: Technical writers can provide input on design decisions from a documentation perspective, advocating for clarity, consistency, and usability.\n  User-Centric Documentation: Early involvement helps ensure that the documentation meets the needs of its target audience from the very beginning.\n  Aligning Documentation with API Strategy: Helps in creating documentation that is not only informative but also aligns with the broader goals of the API program.\n\n\nThe biggest challenge with this approach is keeping track of the changes that are made to prevent the OpenAPI document from drifting away from the actual implementation. Tools like Bump.sh can generate automated diffs, highlighting changes in code or documentation that might impact the other. This fosters better communication between engineers and technical writers, and helps prepare for new releases.\n\nGit Workflow Conflicts: The Case for Separate Repositories\n\nIn some organizations, the workflow of the engineering teams may conflict with that of a technical writer. These conflicts may prevent technical writers from being able to update documentation stored within the same git repository, resulting in waiting for development cycles to be complete. Or, perhaps the changes being made by a technical writer to API documentation is slowing down developers as they are forced to deal with extra feature branches and merging requirements.\n\nIn this case, it may be best to store your API documentation in a separate git repository. Storing API documentation in a separate repository from the code can provide several advantages:\n\n\n  Flexibility in Documentation Management: Allows documentation to be updated independently from the code, facilitating updates and revisions without needing to touch the codebase.\n  Version Control for Documentation: Helps maintain a history of changes specifically for documentation, making it easier to track and revert changes if necessary.\n  Collaboration Across Teams: Enables non-developers, like technical writers and product managers, to contribute more easily to the documentation process.\n\n\nOf course, a separate CI/CD pipeline process will need to be created to support the automatic build and publication process of the API documentation. Plus, API documentation can easily become out of sync with the code.\n\nTo ease this workflow, consider using diff tooling to help identify documentation or code that has changed, requiring updates to one or both git repositories. This makes it easier to plan your technical writing schedule by quickly identifying areas in need of updated documentation.\n\nConclusion\n\nCoordinating API documentation with software engineering is an ongoing challenge. By embracing separate repositories, leveraging OpenAPI overlays, and utilizing automation tools, technical writers can work effectively alongside engineers to deliver exceptional API experiences. This not only improves the quality of the API documentation but also enhances the overall developer experience, leading to a more successful API program."
        },
        {
          "id": "guides-bump-sh-tutorials-migrate-from-redocly",
          "title": "Migrate from Redocly",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Bump-sh tutorials",
          "tags": "",
          "url": "/guides/bump-sh-tutorials/migrate-from-redocly/",
          "content": "Redocly is a hosted OpenAPI documentation SaaS which works in a similar way to Bump.sh: you work on OpenAPI wherever you want, then publish it up to Redocly via various Git hosting providers or Continuous Integration. The fact that it works so similarly means you can switch from Redocly easily, and start getting all the extra benefits of breaking change detection and an API changelog with minimal effort.\n\nStep 1: Find your OpenAPI\n\nMost users will have set Redocly up to pull from GitHub, GitLab, or Bitbucket, making this stage very simple. You already have your OpenAPI in your control. Going to “Settings” &gt; “Source” will remind you exactly which repository, which branch, and which file exactly.\n\n\n\nIf you are using some more complex CI/CD setup and have no idea where your OpenAPI originates, and the people who knew have left the company without a trace, you can export OpenAPI from Redocly.\n\n\n\nGet the downloaded OpenAPI into your source code repository somewhere sensible, and give it a tidy name in the process.\n\nmv ~/Downloads/swagger.yaml ~/src/train-travel-api/openapi.yaml\n\n\nNow you can add this OpenAPI to your source control, which assuming Git is a case of:\n\ncd ~/src/train-travel-api\n\ngit add openapi.yaml\n\ngit commit -m \"docs: imported openapi\"\n\n\nStep 2: Publish OpenAPI as Documentation\n\nInstead of installing a GitHub Application, Bump.sh uses a GitHub Action for deployments. Alternatively you can get creative with the command-line, using a Continuous Integration setup, or even deploying via the Bump.sh API.\n\n# .github/workflows/bump.yml\n\nname: Check &amp; deploy API documentation\n\non:\n  push:\n    branches:\n      - main\n\n  pull_request:\n    branches:\n      - main\n\npermissions:\n  contents: read\n  pull-requests: write\n\njobs:\n  deploy-doc:\n    if: ${{ github.event_name == 'push' }}\n    name: Deploy API documentation on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;BUMP_DOC_ID&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: doc/api-documentation.yml\n\n  api-diff:\n    if: ${{ github.event_name == 'pull_request' }}\n    name: Check API diff on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n      - name: Comment pull request with API diff\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;BUMP_DOC_ID&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: doc/api-documentation.yml\n          command: diff\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n\n\nGrab your Doc ID and Access Token from the Automatic Deployment settings, pop the Doc ID into the GitHub Action and put the API specific BUMP_TOKEN into the GitHub repository Secrets.\n\n\n\nThis workflow will have Bump.sh make previews on Git branches, then deploy the main version of the documentation when a branch is merged. By doing this the pull requests also get change detection, allowing for warnings about breaking changes, and making a simple list of changes to help with design reviews.\n\n\n\nA side benefit of working this way is that you can defer all configuration about how reviews work, what checks should be involved, who is in charge of approving changes, etc. to the pull request settings of your source control.\n\nOnce a guide is published you can view the hosted documentation on Bump.sh, and it will look a little something like this: Train Travel API Documentation.\n\nStep 3: Use Hubs to Replace Developer Portals\n\nIf you were using the Developer Portals feature with Redocly to group together multiple APIs, you can use Hubs to recreate this functionality and provide a landing page for each group of APIs.\n\n\n\nVendor Extension Compatibility\n\nBoth Redocly and Bump.sh support vendor extensions, which are non-standard additions to the OpenAPI, starting with an x- like x-logo. Some extensions are based on common conventions, but some are vendor specific, and some are there to support functionality that was missing in older versions of OpenAPI that are no longer needed.\n\nBump.sh Supports\n\n\n  \n    x-topics - Add more context paragraphs in your generated documentation. Learn more.\n  \n  \n    x-codeSamples - Add your own code samples in one or more programming languages to your documentation. Learn more.\n  \n  \n    x-beta - Allows you to identify some components of your documentation as beta. Learn more.\n  \n\n\nTo help you with your migration, here is a full list of the extensions Redocly support, with suggestions of alternative approaches where they exist.\n\n\n  \n    \n      Location\n      Extension Keyword\n      Bump.sh\n      Alternative\n    \n  \n  \n    \n      Root\n      x-servers\n      ❌\n      Use the servers keyword added in OAS3.0\n    \n    \n      Root\n      x-tagGroups\n      ❌\n       \n    \n    \n      Tag Group\n      x-ignoredHeaderParameters\n      ❌\n       \n    \n    \n      Info\n      x-logo\n      ❌\n      Upload a logo using customization settings.\n    \n    \n      Tag\n      x-traitTag\n      ❌\n       \n    \n    \n      Tag\n      x-displayName\n      ❌\n      Use human-readable tag names.\n    \n    \n      Operation\n      x-codeSamples\n      ✅\n       \n    \n    \n      Parameter\n      x-examples\n      ❌\n      Use the examples keyword added in OAS3.0.\n    \n    \n      Response\n      x-summary\n      ❌\n       \n    \n    \n      Schema\n      x-nullable\n      ❌\n      Use the nullable keyword added in OAS3.0 or type: null added in OAS3.1\n    \n    \n      Schema\n      x-extendedDiscriminator\n      ❌\n      Discriminator is confusing, poorly supported, and best avoided. Use oneOf instead.\n    \n    \n      Schema\n      x-additionalPropertiesName\n      ❌\n      Both additionalProperties and newer unresolvedProperties now accept a schema that can contain this extra information and more.\n    \n    \n      Schema\n      x-explicitMappingOnly\n      ❌\n      Discriminator is confusing, poorly supported, and best avoided. Use oneOf instead."
        },
        {
          "id": "guides-bump-sh-tutorials-try-requests-in-insomnia",
          "title": "Try HTTP Requests with Insomnia",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Bump-sh tutorials",
          "tags": "",
          "url": "/guides/bump-sh-tutorials/try-requests-in-insomnia/",
          "content": "Bump.sh now includes its own API Explorer, allowing you to test your API directly within your documentation. Give it a try!\n\n\nSeeing how an API works is the first step in an end-users journey to using the API, and the second step is making some test requests to get a feel for how it works. Some people like to do this with code, so code samples will be a good start for them, especially if the API has an SDK. Other people like to do this with interactive HTTP clients, like Insomnia.\n\nInsomnia is a lightweight API console, which allows users to create or import “collections” of requests and responses. This concept is similar to OpenAPI, but instead of creating a list of metadata about requests/responses, it focuses on saving actual requests and responses, like a whole bunch of bookmarks in a browser.\n\nThis guide will show you how you can get started by building an Insomnia Collection from the same OpenAPI that powers your Bump.sh documentation, then create a “Run in Insomnia” button to link users to this collection from your Bump.sh API docs.\n\nStep 1) Sign up for Insomnia\n\nFirst you’ll need to make a free account with Insomnia. Head over to Insomnia’s website and click Get Started for Free.\n\nStep 2) Download Insomnia\n\nWhen your account has been created you will see a simple dashboard, which is mostly for account management. To do anything meaningful in Insomnia you need to download the Desktop application.\n\n\n\nStep 3) Create a Collection\n\nWhen you open up Insomnia you will have an empty project, a blank canvas which needs to know about our API. The next step is to create an Insomnia collection, which will store all of the example requests and responses to help show off how the API should work.\n\nThe goal is to convert our OpenAPI into a Collection which will be full of requests and responses for the OpenAPI operations that have already been defined, which will avoid having to manually create all these requests and responses. Thankfully Insomnia supports OpenAPI too.\n\nBump.sh users will already have an OpenAPI document somewhere in the form of a openapi.yaml or openapi.json, and this will be on a Git repository or in the filesystem somewhere.\n\n\n\nVia Git\n\nYou can Git Clone your repository through Insomnia if you have a paid “Team” account, which will help keep the project in sync over time.\n\nVia Import\n\nFree users can create a collection by clicking “New Collection”, and giving it a name.\n\n\n\nOnce the collection is made you can click on the “…” and click “Import”.\n\n\n\nThen select OpenAPI document.\n\n\n\nThis approach means the collection will get out of date over time, but will help you at least see how this whole thing works, and Git sync can be enabled later if you feel like its worth upgrading.\n\nWhichever way you do it, you’ll see the new Insomnia Collection created for your API, so now we can play around with it.\n\nStep 4) Fixing Environment Variables\n\nInsomnia supports the idea of Environments, which means you can have one for “Development”, one for “Production”, others for sandboxes, mock servers, etc. It will create an OpenAPI Env which is pre-populated with various values from the OpenAPI document you imported from. To select this, click the dropdown which defaults to Base Environment, and select “OpenAPI env”.\n\nThe OpenAPI import for Insomnia currently seems to have a few snags, because on importing the Train Travel API it was showing errors about bad environment variables.\n\n\n\nClicking on that variable in the URL will show the following error:\n\n\n  “attempted to output null or undefined value”\n\n\nThis can be fixed by selecting the _.host variable.\n\n\n\nDoing this for each operation is not ideal, but the Insomnia team are aware of the problem and hopefully this will be resolved soon.\n\nStep 5) Updating Broken Parameters\n\nAnother quirk with the Insomnia import comes from it created environment variables for all path parameters, which are then not found in the environment so more errors appear.\n\n\n\nFixing this requires the URL to be edited, removing that broken variable with the backspace key, and writing in :parameterName. You’ll know it’s worked when a box appears in the Path Parameters list.\n\n\n\nWhilst these bugs are fixed up, it’s a good chance to work through the parameters and add example values, or pop in dynamic ones. See what autocomplete suggestions pop up when you type things like “uuid” or “email”, or any other faker value.\n\nStep 6) Publish Collection\n\nExport the collection as a Insomnia JSON, and publish that somewhere public.\n\n\n\nOne place for that might be in an insomnia/ directory in your main API source code and/or documentation repository.\n\n\n\nWherever it goes, make sure it is accessible via a public URL, which if it’s on a GitHub public repository can be found by click on the file and clicking Raw:\n\n\n\nThis gives you a URL for the Insomnia collection like this:\n\nhttps://raw.githubusercontent.com/bump-sh-examples/train-travel-api/main/insomnia/Insomnia_2024-05-27.json\n\n\nNow we can use that URL for the next step.\n\nStep 7) Create “Run in Insomnia” Button\n\nBuried in the Preferences page under Data there’s a link to the Create Run Button applet. Open this page up and copy that public URL into the text box.\n\n\n\nTake that Markdown, and pop it into your OpenAPI somewhere. The easiest place to put it is right in the info.description property.\n\nopenapi: 3.1.0\ninfo:\n  title: Train Travel API\n  description: |\n    API for finding and booking train trips across Europe.\n\n    ## Run in Insomnia\n\n    Experiment with this API on Insomnia, using our Insomnia Collection.\n    \n    [![Run in Insomnia}](https://insomnia.rest/docs/images/run.svg)](https://insomnia.rest/run/?label=Train%20Travel%20API&amp;uri=https%3A%2F%2Fraw.githubusercontent.com%2Fbump-sh-examples%2Ftrain-travel-api%2Fmain%2Finsomnia%2FInsomnia_2024-05-27.json)\n\n\n\n  A better place for this button might be a dedicated Topic, using the x-topics extension supported by Bump.sh documentation.\n\n\nTo see how this looks in Bump.sh before pushing it live, you can use the Bump.sh CLI’s preview command.\n\nnpx bump-cli preview openapi.yaml --live\n\n\nThe command will return a link, click that and see how the documentation looks.\n\n\n\nWhen you (or your users) click the new Run in Insomnia button, they’ll be asked to import your collection to their own workspace, and now they can interact with your API on their own computers making requests with all the parameters and authentication options set up for them!"
        },
        {
          "id": "guides-bump-sh-tutorials-make-your-apis-discoverable-with-apisjson",
          "title": "Make your APIs Discoverable with APIs.json",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Bump-sh tutorials",
          "tags": "",
          "url": "/guides/bump-sh-tutorials/make-your-apis-discoverable-with-apisjson/",
          "content": "You’ve built a brilliant API, and you’ve taken the time to document it, but how do you get this API advertised to potential users? After all, the more users that discover your API, the more business you’re getting, so it’s worth thinking about discoverability beyond a tweet and a blog.\n\nAPI Marketplaces like API Layer and RapidAPI let you list APIs in a searchable directory so potential users can find them. There are quite a few API Marketplaces, and they seem to come and go over time, which means adding every one of your APIs to every marketplace (and keeping it all up-to-date) can be outrageously time consuming. Years ago when most companies only had one API it might be an easy guess: https://example.org/api or https://api.example.org/, but as more and more APIs start popping up from different teams with mismatched creative codenames, keeping track of APIs can become more difficult internally and externally.\n\nMany API teams have created “API Catalogs” to list their own APIs, which is often a hand-built system or using something like Bump.sh’s Hubs, so it feels like the most logical approach is to use your API Catalog to create a shareable directory of your APIs to other marketplaces and tools.\n\nAs with everything in the world of APIs there’s a brilliant specification for doing this, but as with everything it has a very vague name: APIs.json. Not to be confused with JSON:API, APIs.json is a specification that aims to solve API discoverability, created by Kin “API Evangelist” Lane and Steven Willmott.\n\nHow does APIs.json work?\n\nAPIs.json contains two simple concepts to help software find your APIs and learn about them.\n\n\n  a “Well Known” URL of /apis.json (or /apis.yaml).\n  a specific JSON/YAML data format for that URL.\n\n\nThink of the /apis.json URL like /robots.txt or /sitemap.xml. In the same way that various crawlers and automated agents will use that information to find out what should be indexed on a particular domain, the /apis.json URL can help various agents discover what APIs are available on a given domain.\n\nThe data for that endpoint looks a little something like this.\n\n{\n  \"name\": \"My Train Company\",\n  \"description\": \"## Welcome to the Bump.sh demo!\\r\\n\\r\\n*Bump.sh is much more than stunning documentation, for all your APIs.*\\r\\n\\r\\nBrowse through **API Hubs** and our **sleek documentation experience**. Try out our **search feature**, and discover how **Bump highlights changes** when you iterate on APIs development.\\r\\n\\r\\n[Signup](https://bump.sh/users/sign_up) to deploy your own API docs in minutes, and discover all configuration options and integrations.\",\n  \"url\": \"https://demo.bump.sh/apis\",\n  \"created\": \"2019-12-12\",\n  \"modified\": \"2024-05-16\",\n  \"specificationVersion\": \"0.16\",\n  \"apis\": [\n    {\n      \"name\": \"Train Travel API\",\n      \"humanUrl\": \"https://demo.bump.sh/doc/trainbook\",\n      \"created\": \"2024-05-16\",\n      \"modified\": \"2024-05-16\",\n      \"description\": \"API for finding and booking train trips across Europe.\",\n      \"version\": \"1.0.0\",\n      \"properties\": [\n        {\n          \"type\": \"x-access-level\",\n          \"data\": \"public\"\n        },\n        {\n          \"type\": \"x-definition-type\",\n          \"data\": \"REST\"\n        }\n      ]\n    },\n    // ... other APIs ...\n  ]\n}\n\n\nThere’s a lot to look at here, but basically there is some metadata about the collection of APIs, then an array of APIs. Let’s start with the top level properties that describe the collection itself, and this is based on v0.16 of the specification.\n\n\n  name (required): text string of human readable name for the collection of APIs\n  description (required): text human readable description of the collection of APIs.\n  type (optional): Type of collection (Index [of a single API], Collection [of multiple APIs], Blueprint [of a new API]).\n  image (optional): Web URL leading to an image to be used to represent the collection of APIs defined in this file.\n  url (required): Web URL indicating the location of the latest version of this file so it can be more portable.\n  tags (optional): a handful of key words and phrases that describe your API collection.\n  created (required): date of creation of the file.\n  modified (required): date of last modification of the file.\n  specificationVersion (required): version of the APIs.json specification in use.\n  apis (optional): list of APIs identified in the file.\n\n\nThe apis collection being optional feels a little funny, but APIs.json can describe a single API as well as a collection of APIs. There’s not many examples of APIs.json being used this way, so we’ll stick to using it for a collection of APIs.\n\n\n  name (required): name of the API\n  description (required): human readable text description of the API.\n  image (optional): URL of an image which can be used as an “icon” for the API if displayed by a search engine.\n  humanUrl (optional): Web URL corresponding to human readable information about the API.\n  baseUrl (optional): Web URL corresponding to the root URL of the API or primary endpoint.\n  version (optional): String representing the version number of the API this description refers to.\n  tags: (collection) (optional): this is a list of descriptive strings which identify the API.\n\n\nThe humanUrl can point to some API documentation, whether that’s some hand-written guides, API reference documentation generated by OpenAPI, or anything else.\n\nThe baseUrl is where the API domain actually lives, so any robots seeing this APIs.json file can go off and start actually interacting with the API automatically.\n\nThe version could be the latest version of the API that’s deployed just so robots know things have changed, perhaps being more wary of a major version change, or using it to keep talking to different versions of an API.\n\nThen tags appears on the API level, helping make clear that “Funny Name Our Team Liked API” actually handles [\"payments\", \"contracts\"].\n\nYou can build all of this yourself, or you can host your documentation with Bump.sh and let us do it.\n\nWorking with APIs.json in Bump.sh\n\nBump.sh will automatically create an /apis.json for anyone using Documentation Hubs. Hubs are a way of grouping APIs with shared access controls, grouping together related or relevant API documentation. A Hub could be for a team, a department, or for a product line that has multiple APIs that all communicate with each other.\n\nHow you use them is up to you, but to work with APIs.json you’ll want to make a Hub.\n\n\n\nThen add some APIs to that Hub on the API’s Settings page.\n\n\n\nNow head over to the Dashboard and click View Hub, and you’ll be taken to a URL like https://bump.sh/&lt;org-slug&gt;/hub/&lt;hub-slug&gt;. To get the APIs.json endpoint you can append /apis.json onto the end of that URL, to produce https://bump.sh/&lt;org-slug&gt;/hub/&lt;hub-slug&gt;/apis.json.\n\n\n\nSubmit APIs.json to APIs.io\n\nGreat! Now, the first thing we should do with this APIs.json is use it to help people find our APIs by submitting our APIs to APIs.io, which is a new API Marketplace that describes itself as “the search engine for APIs”.\n\nHead over to APIs.io: Add, and paste your link into the URL box.\n\n\n\nIf you want to automate submissions and updates then you can use the APIs.io API, or grab one of their SDKs, but it may well be easier to just submit the form.\n\nPrivate Bump.sh Hubs\n\nIf you want to make your hubs private but still want to access the data in APIs.json, then you can access it via the Bump.sh API using the Hubs endpoint.\n\ncurl -X GET https://bump.sh/api/v1/hubs/&lt;hub-slug-or-id&gt; -H \"Authorization: Token &lt;access-token&gt;\"\n\n\nThis makes it slightly trickier to pass to some software, but private things are definitively harder to discover. At least you can use this document for use-cases that involve code or CLI once you have it.\n\nOther Uses for APIs.json\n\nAPIs.json has been around for a decade, but is only starting picking up steam recently thanks to tooling starting to support it. Support is still limited, but there are countless potential use cases that can be handled with this specification.\n\nBump.sh customers are already using it for quality assurance on their developer portals, making sure all of their internal APIs are published, and available at all times. Using Continuous Integration they can check the generated APIs.json, and make sure their API documentation is there and visible on the expect URLs.\n\nThere are also clear implications for monitoring, if each API can advertise its /health endpoints you can pass that to monitoring software, or even just ping each endpoint yourself to look for downtime or issues listed in the health check.\n\nThe days of only have one API to manage are gone, and now we’re in an age of “API Sprawl”. Let’s all get better at keeping track of our APIs, helping end-users find documentation, support contacts, and any other information in a sensible scalable way, without having to rely on search engines or begging for help on social media."
        },
        {
          "id": "guides-technical-writing-using-readme-style-api-documentation-to-enhance-your-api-design",
          "title": "Using README-style API Documentation to Enhance Your API Design",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Technical Writing",
          "tags": "",
          "url": "/guides/technical-writing/using-readme-style-api-documentation-to-enhance-your-api-design/",
          "content": "A common pitfall of API documentation is focusing solely on documenting individual operations in isolation, neglecting the complexity of real-world workflows that involve chaining multiple operations together. While individual API operations often appear well-designed, the true test lies in how users can orchestrate these operations to achieve specific goals. This can lead to confusion and frustration for developers, hindering API adoption and ultimately compromising its effectiveness.\n\nThis article proposes a user-centric approach to API documentation, leveraging README-style documentation to bridge the gap between individual operations and practical usage scenarios. We will explore what README-style API documentation looks like, the value it provides, and how you can link this style of documentation into your Bump.sh-generated documentation set to improve the developer experience of your APIs.\n\nOpenAPI Specification: Defining the Technical Foundation\n\nBefore diving into README-style documentation, it’s crucial to establish a solid foundation using the OpenAPI Specification (OAS). This industry-standard format provides a machine-readable description of your API, including its endpoints, parameters, request/response structures, and error codes. Here’s an example OAS snippet for a hypothetical e-commerce API:\n\npaths:\n  /products/{productId}:\n    get:\n      summary: Get product information by ID\n      parameters:\n        - name: productId\n          in: path\n          required: true\n          type: integer\n      responses:\n        200:\n          description: Product details\n          content:\n            application/json:\n              schema:\n                type: object\n                properties:\n                  id:\n                    type: integer\n                  name:\n                    type: string\n                  ...\n  /orders:\n    post:\n      summary: Create a new order\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              properties:\n                customer_id:\n                  type: integer\n                items:\n                  type: array\n                  items:\n                    type: object\n                    properties:\n                      product_id:\n                        type: integer\n                      quantity:\n                        type: integer\n      responses:\n        201:\n          description: Order created\n          content:\n            application/json:\n              schema:\n                type: object\n                properties:\n                  id:\n                    type: integer\n  /orders/{orderId}/items:\n    post:\n      summary: Add items to an existing order (updated endpoint)\n      parameters:\n        - name: orderId\n          in: path\n          required: true\n          type: integer\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              properties:\n                items:\n                  type: array\n                  items:\n                    type: object\n                    properties:\n                      product_id:\n                        type: integer\n                      quantity:\n                        type: integer\n      responses:\n        200:\n          description: Items added to order\n\n\nNow that we understand the API a bit better, let’s look at how reference documentation generated from this OpenAPI Specification can create problems for your API consumers.\n\nThe Problem with Traditional API Documentation\n\nTraditional API documentation often follows a technical reference approach, meticulously detailing each individual endpoint with its parameters, request/response formats, and error codes. While this information is valuable, it fails to capture the bigger picture of how users actually interact with the API.\n\nHere’s why this approach falls short:\n\n\n  \n    Limited Context: Users are left guessing how to combine individual operations to achieve desired outcomes.\n  \n  \n    Workflow Disconnect: The documentation doesn’t reflect the natural flow of user tasks, making it difficult to understand how to achieve specific goals.\n  \n  \n    Lack of Practical Guidance: Real-world examples and code snippets are often missing, leaving users to figure out the integration on their own.\n  \n\n\nThis can lead to:\n\n\n  \n    Steeper Learning Curve: Users struggle to grasp the overall API functionality and spend valuable time piecing together workflows from scattered documentation.\n  \n  \n    Usability Issues: Confusion and frustration arise when users encounter unexpected behavior or limitations not evident from individual operation descriptions.\n  \n  \n    Delayed Adoption: Users may be hesitant to adopt the API if the documentation doesn’t provide a clear path to achieving their goals.\n  \n\n\nREADME-style API Documentation: A User-Centric Approach\n\nREADME-style API documentation shifts the focus from individual operations to user workflows. It presents API usage through practical scenarios, demonstrating how operations are chained together to achieve specific outcomes.\n\nHere’s what it entails:\n\n\n  \n    Workflow-Driven: Documents showcase common user workflows or use cases, outlining the sequence of operations needed to accomplish specific tasks.\n  \n  \n    Practical Examples: Code snippets and step-by-step instructions guide users through the process, lowering the barrier to entry.\n  \n  \n    Real-World Focus: Examples are tailored to actual use cases, providing context and relevance for users.\n  \n\n\nThis approach offers several benefits:\n\n\n  \n    Improved Usability: Users can easily understand how to achieve their goals by following documented workflows.\n  \n  \n    Early Feedback: Observing user interactions with documented workflows can reveal design flaws and areas for improvement before the cost of change becomes higher due to the need for code changes.\n  \n  \n    Enhanced Getting Started Guide: README-style documentation provides a practical foundation for building a comprehensive getting started guide.\n  \n\n\nImplementing README-style API Documentation\n\nHere are some practical steps to get started:\n\n\n  \n    Identify Common User Workflows: Analyze user needs and identify common tasks they want to achieve using the API.\n  \n  \n    Craft Clear and Concise Workflows: Document each workflow with a clear narrative, outlining the sequence of operations involved.\n  \n  \n    Utilize Code Snippets and Examples: Provide code snippets in popular programming languages to illustrate the practical implementation of each workflow.\n  \n  \n    Integrate with Existing Tools: Leverage existing documentation platforms and tools to seamlessly integrate README-style documentation with your API reference.\n  \n\n\nFor many APIs, there may be a large number of workflows that could be generated. When this is the case, start with the most common workflows and expand as time allows.\n\nExample: User Workflow with README-style Documentation\n\nLet’s consider our hypothetical e-commerce API, above, by documenting a common workflow of viewing product details, creating a new order, and adding the product to the order. Here’s an example of a README-style workflow demonstrating this workflow:\n\nWorkflow: Creating an Order and Adding Items\n\nThis workflow demonstrates how to create a new order and add items to it using the e-commerce API:\n\nStep 1.  Get Product Information:\n\nUse the /products/get operation to retrieve information about the desired product(s).\n\nGET /products/123\n\n\nStep 2. Create a New Order:\n\nUse the /orders/create operation to create a new order.\n\nPOST /orders \n{  \n  \"customer_id\": 1,  \n  \"items\": []\n}\n\n\nStep 3. Add Items to Order:\n\nUse the /orders/{orderId}/items operation to add retrieved products to the newly created order.\n\nPOST /orders/1/items\n\n[  \n  {    \"product_id\": 123,    \"quantity\": 2  },  \n  {    \"product_id\": 456,    \"quantity\": 1  }\n]\n\n\nThis workflow provides a clear step-by-step guide, eliminating ambiguity and simplifying the process for users.\n\nUsing Bump.sh x-topic extensions\n\nThe x-topic extensions from Bump.sh offers a method of including these kinds of documentation details into your OpenAPI Specification document. In this case, you can enclose your content using Markdown into the OpenAPI Specification and allow the Bump.sh platform to render it.\n\nBelow is an example of how this might be used, by playing x-topics at the top-level of your OpenAPI Specification document:\n\nx-topics:\n- title: \"Workflow Example: Creating an Order and Adding Items\"\n  content: |\nThis workflow demonstrates how to create a new order and add items to it using the e-commerce API:\n\n**Step 1.  Get Product Information:**\n    \nUse the `/products/get` operation to retrieve information about the desired product(s).\n    \n\n```http\nGET /products/123\n\n\nStep 2. Create a New Order:\n…\n\nIf you prefer not to include your workflows in your OpenAPI specification, you can also leverage [external JSON refs](/help/specification-support/references/#external-references) to decouple your workflow-based documentation from your API specification. \n\n## OpenAPI Workflow Specification: The Future of API Workflows\n\nThe OpenAPI Initiative is currently developing a dedicated OpenAPI Workflow Specification that aims to formally define and document API workflows. This specification will provide a structured approach to capturing the sequence of operations involved in achieving specific tasks, further enhancing the user experience and promoting better API design.\n\nHere's a potential example of how the OpenAPI Workflow Specification could be used to capture the \"Creating an Order and Adding Items\" workflow:\n\n```yaml\nworkflows:\n  createOrderAndAddItems:\n    description: Creates a new order and adds items to it.\n    steps:\n      - operationId: getProductById\n        path: /products/{productId}\n      - operationId: createOrder\n        path: /orders\n      - operationId: addItemsToOrder\n        path: /orders/{orderId}/items\n\n\nThis example demonstrates how the workflow defines the sequence of operations involved, along with their corresponding operation IDs and paths. While this extension to the OpenAPI Specification is in its early days as of the time of this writing, it shows considerable promise. You can find out more by visiting the OAI Workflows SIG.\n\nConclusion\n\nREADME-style API documentation empowers developers to design APIs that prioritize user experience. By focusing on real-world workflows and providing practical guidance, it fosters better understanding, reduces friction, and ultimately leads to more effective and user-friendly APIs.\n\nAdopting this approach allows developers to:\n\n\n  \n    Bridge the gap between technical specifications and practical usage.\n  \n  \n    Gather valuable user feedback through documented workflows.\n  \n  \n    Create comprehensive and user-friendly API documentation that promotes adoption.\n  \n\n\nInvesting in README-style documentation leads to a more intuitive and user-centric API experience, ultimately driving better user engagement and API success. By leveraging the x-topics extension from Bump.sh and the OpenAPI Workflow Specification, you will have multiple options to provide a standardized and machine-readable format for capturing and sharing complex workflows."
        },
        {
          "id": "guides-technical-writing-the-role-of-technical-writer-in-api-documentation",
          "title": "The Role of Technical Writer in API Documentation",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Technical Writing",
          "tags": "",
          "url": "/guides/technical-writing/the-role-of-technical-writer-in-api-documentation/",
          "content": "Until recently, technical writers focused on delivering manuals for software, often in PDF or HTML format. These manuals consisted of step-by-step guidance for using software, often accompanied by annotated screenshots. The technical writer was critical for ensuring end users were able to use the software effectively and efficiently while reducing the vendor’s support costs.\n\nWith the growth of APIs, technical writers are in heavy demand once again. In this article, we will explore the new role of technical writers, the skills they need to participate in writing API documentation, and the challenges they may encounter as they collaborate with development teams.\n\nWhy technical writers are critical for APIs\n\nTechnical writers are an invaluable part of an API delivery team. They bring a fresh perspective as they look for ways to turn the technical details of an API into clear documentation for developers. Because they often take a user-centric approach to documentation, they help to focus the API documentation on the needs and questions of the API consumer. This can reduce the learning curve and support a positive developer experience.\n\nTheir value doesn’t stop with writing documentation. When given the opportunity, technical writers can turn customer support engagements and developer feedback into recommendations for improvements. This may include documentation improvements, but it may also include design improvements that can benefit current and future API consumers.\n\nI’ve personally experienced tremendous value by involving technical writers early in the API design process. Because they are always evaluating how a specific API operation will be documented, they shed light on API design elements that may be confusing. Questions around the purpose and intended use of each API operation and the API as a whole can help to improve the API design.\n\nEssential API skills for technical writers\n\nFor technical writers who have a software developer background, the transition to API documentation is seamless. However, technical writers who have been more product or user-focused may find themselves quickly learning the essentials of APIs. This may include learning how HTTP works, how to leverage YAML and the OpenAPI Specification to capture API reference details, and how to author with asciidoc or markdown rather than XML-based authoring tools such as oXygen.\n\nTechnical writers also benefit from learning how to use API client tools, such as Bump.sh, Postman, Insomnia, and Hoppscotch. Command-line tools such as cURL may also be useful but aren’t always necessary as it depends on the preferences of the intended audience that will use the API.\n\nThey also benefit from learning source control systems such as git or platforms such as GitHub and GitLab to better manage their artifacts and integrate with continuous integration pipelines. Understanding workflows around these systems is also important, including branching and forking documentation for upcoming releases without negatively impacting existing documentation.\n\nIn some cases, it can be beneficial for technical writers to learn one or more of the following languages: Java, Python, GoLang, Ruby, JavaScript, Objective-C, Swift, and PHP. However, this isn’t necessary as development teams can help compose examples in these languages to be included in API documentation.\n\nChallenges that technical writers face\n\nTechnical writers face a number of challenges as they shift into an API-centric environment. The most common challenge is being able to engage with teams early and often. Most development teams focus on the code rather than documentation, so they don’t consider the involvement of technical writers until late in the development process. By this point, it can be difficult or impossible to make any design changes surface during the documentation effort.\n\nAnother challenge is the increased speed of delivery. Today’s delivery processes encourage frequent releases that are measured in days or weeks rather than months. This puts tremendous pressure on technical writers to produce what may be considerable documentation in a short amount of time.\n\nA single technical writer for a small API may be able to keep documentation updated. If the organization is large and manages multiple APIs, the challenge increases beyond the capabilities of even the most talented technical writer. Larger organizations require multiple technical writers of varying levels of expertise to support larger portfolios of APIs.\n\nMany of these challenges can be overcome by ensuring that technical writers are involved earlier in the design and delivery process. They should be considered first-class team members rather than a siloed team that has APIs thrown at them at the last minute for a quick-and-dirty documentation effort.\n\nConclusion\n\nAs APIs become more prevalent, the demand for skilled technical writers who can bridge the gap between complex technical information and the end-user’s understanding grows. Their ability to provide clear, concise, and user-centric documentation is essential for facilitating effective developer experiences and enhancing API usability. Finally, remember that API documentation is the user interface for developers. Therefore, technical writers are instrumental in the success of any API."
        },
        {
          "id": "guides-technical-writing-improving-schema-component-documentation-in-openapi-documents",
          "title": "Improving Schema Component Documentation in OpenAPI Documents",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Technical Writing",
          "tags": "",
          "url": "/guides/technical-writing/improving-schema-component-documentation-in-openapi-documents/",
          "content": "The OpenAPI schema component offers API designers and technical writers an opportunity to define the structure of an API’s resource representation structure and is a primary reference point for both internal developers and external consumers. Improving documentation in this area can significantly enhance usability and reduce integration times, errors, and frustration. In this article, we will explore tips and examples on how to effectively document schema components in OpenAPI specification documents.\n\n1. Including Examples in Schema Components\n\nOne common shortfall in schema documentation is the absence of examples. Examples are vital as they provide a tangible way to understand the API’s expected data structures and responses. They help developers quickly grasp how the API works in real-world scenarios, which is crucial for effective implementation.\n\nExample:\ncomponents:\n  schemas:\n    User:\n      type: object\n      properties:\n        id:\n          type: integer\n          format: int64\n          example: 101\n        username:\n          type: string\n          example: \"janedoe\"\n        email:\n          type: string\n          format: email\n          example: \"janedoe@example.com\"\n      required:\n        - id\n        - username\n\nThis example clearly shows what kind of data is expected for each field within a User object, making it easier for developers to construct requests and understand responses.\n\nNote: When documenting schema components, try to unify the dataset across different schema elements. This will provide greater consistency for the reader and help them understand how data values relate to each other across the API’s resources.\n\n2. Documenting Formats Not Covered by OpenAPI Specification\n\nThe OpenAPI specification provides a set of predefined formats for data types, like integer, string, and boolean. However, it doesn’t cover every possible data format that an API might use, such as specific date/time formats or regular expressions for string validation.\n\nExample:\ncomponents:\n  schemas:\n    Event:\n      type: object\n      properties:\n        startDate:\n          type: string\n          format: date-time\n          example: \"2024-05-05T13:45:00Z\"\n          description: \"The start date and time of the event in ISO 8601 format.\"\n        zipCode:\n          type: string\n          pattern: '^\\d{5}(-\\d{4})?$'\n          example: \"90210\"\n          description: \"US 5-digit zip code with optional dash plus 4-digit extension.\"\n\nHere, the startDate property includes a description that specifies the exact format expected, which is ISO 8601 for date-time. Similarly, zipCode uses a regular expression that is explained and exemplified, making it clear what the field expects without the need for the reader to be an expert in regular expressions.\n\n3. Clarifying Required and Optional Fields\n\nA frequent source of confusion in API documentation is not clearly marking which fields are required and which are optional. This can lead to failed API requests if the client does not provide all necessary information.\n\nExample:\ncomponents:\n  schemas:\n    Order:\n      type: object\n      properties:\n        orderId:\n          type: integer\n          format: int64\n          example: 120394\n        paymentMethod:\n          type: string\n          example: \"credit card\"\n          description: \"Payment method used for the order.\"\n      required:\n        - orderId\n        - paymentMethod\n\nIn this example, orderId and paymentMethod are explicitly marked as required, making it clear that these fields must be included in any requests.\n\n4. Documenting Mutually Exclusive Fields and Discriminator Fields\n\nAnother area that often lacks clarity in API documentation is the handling of discriminator fields, which help in polymorphic serialization/deserialization.\n\nExample:\ncomponents:\n  schemas:\n    Payment:\n      type: object\n      discriminator:\n        propertyName: paymentType\n        mapping:\n          card: '#/components/schemas/CardPayment'\n          paypal: '#/components/schemas/PaypalPayment'\n      properties:\n        paymentType:\n          type: string\n          enum:\n            - card\n            - paypal\n      required:\n        - paymentType\n\n\nIn this schema, the paymentType field acts as a discriminator that determines whether the CardPayment or PaypalPayment schema should be used, each of which would be defined separately in the document. This approach not only supports clearer documentation but also facilitates better client-side code generation.\n\n5. Using the oneOf Schema Descriptor for Legacy APIs\n\nDocumenting legacy APIs that were designed to handle a variety of scenarios can be challenging. For scenarios when responses may vary depending on the state of the resource being queried, the oneOf keyword allows specification of multiple possible schemas for a single response. This helps technical writers to capture these more complex situations often found in legacy systems.\n\nExample:\npaths:\n  /users/{userId}:\n    get:\n      summary: Retrieves user account details based on account status.\n      parameters:\n        - name: userId\n          in: path\n          required: true\n          description: Unique identifier of the user.\n          schema:\n            type: string\n      responses:\n        '200':\n          description: A detailed information object about the user's account.\n          content:\n            application/json:\n              schema:\n                oneOf:\n                  - $ref: '#/components/schemas/ActiveAccount'\n                  - $ref: '#/components/schemas/SuspendedAccount'\n                  - $ref: '#/components/schemas/ClosedAccount'\n\n\nIn this example, the operation may return different responses based on the status of the Account resource. A schema for each variation is provided using the oneOf keyword. Developers and code generators can then take the appropriate steps to handle the kind of response that comes back based on the status of the account.\n\n6. Leveraging the allOf Schema Descriptor in OpenAPI for Mixed Responses\n\nThe allOf keyword is a powerful tool for documenting APIs that return mixed or composite responses, combining properties from multiple schemas into a single unified schema.\n\nExample:\npaths:\n  /users/{userId}/details:\n    get:\n      summary: Retrieves comprehensive user details including account settings and permissions.\n      parameters:\n        - name: userId\n          in: path\n          required: true\n          description: Unique identifier of the user.\n          schema\n\n:\n            type: string\n      responses:\n        '200':\n          description: A comprehensive object containing user details, settings, and permissions.\n          content:\n            application/json:\n              schema:\n                allOf:\n                  - $ref: '#/components/schemas/UserBase'\n                  - $ref: '#/components/schemas/UserSettings'\n                  - $ref: '#/components/schemas/UserPermissions'\n\n\nWhile less-than-ideal, this approach may be necessary to properly document the behavior of a poorly designed API that has to handle a variety of implementation decisions.\n\n7. Providing Comprehensive Details\n\nFinally, schema documentation should not ignore other miscellaneous but essential details, such as minimum and maximum values for numeric fields, length constraints for strings, and enumerations that list possible values for a field.\n\nExample:\ncomponents:\n  schemas:\n    Product:\n      type: object\n      properties:\n        price:\n          type: number\n          format: double\n          minimum: 0.01\n          maximum: 1000.00\n          example: 15.75\n        status:\n          type: string\n          enum:\n            - available\n            - discontinued\n          example: \"available\"\n          description: \"Availability status of the product.\"\n\n\nThis schema clearly outlines the constraints on the price field and provides an enumeration for status, helping developers understand the limits and possible values.\n\nConclusion\n\nImproving the schema component documentation in OpenAPI documents is crucial for developing effective and user-friendly APIs. By including detailed examples, properly documenting non-standard formats, clarifying the requirements of fields, handling discriminator and mutually exclusive fields effectively, and providing comprehensive details, you can greatly enhance the developer experience and reduce the likelihood of errors in API integration. This leads to faster development cycles, fewer support questions, and a more robust integration process."
        },
        {
          "id": "guides-technical-writing-enriching-your-openapi-info-documentation-for-understanding",
          "title": "Enriching Your OpenAPI Info Documentation for Understanding",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Technical Writing",
          "tags": "",
          "url": "/guides/technical-writing/enriching-your-openapi-info-documentation-for-understanding/",
          "content": "OpenAPI (OAS) specifications play a crucial role in API development, serving as the foundation for clear communication between developers and consumers. While technical details within the specification are essential, the info section often gets overlooked, leading to a missed opportunity to provide valuable context and understanding.\n\nThis article looks at the importance of a well-crafted info section and offers practical strategies for enriching its content, specifically focusing on the name and description fields, as well as highlighting the importance of detailed tag descriptions. Finally, we will explore ways to organize our API operations using tags to make it easier to understand and get started consuming your API.\n\nThe Power of a Comprehensive info Section\n\nThe info section within an OAS document serves as the introductory point for developers. When properly written, it provides the developer with a high-level overview of the API’s purpose, capabilities, and intended use cases. A well-defined info section offers several benefits:\n\n\n  \n    Improved Discoverability: Clear and concise information in the name and description fields helps developers understand the API’s functionality and its potential value within their projects. This leads to improved discoverability and adoption.\n  \n  \n    Enhanced Context: A detailed description provides valuable context about the API’s purpose, its target use cases, and the overall problem it aims to solve. This context helps developers make informed decisions about integrating the API into their solutions.\n  \n  \n    Streamlined Onboarding: A comprehensive info section acts as a starting point for developers, equipping them with the necessary information to begin exploring the API’s functionalities and specifications. This reduces the time and effort required for onboarding.\n  \n\n\nCrafting a Compelling API Name\n\nImproving the info section begins with a great name. The name field within the info section should be more than just a single word or a generic identifier. It should provide a clear and concise description of the API’s core functionality. A well-chosen name serves as the first impression for developers, influencing their initial perception and potentially impacting adoption.\n\nA clear and descriptive name:\n\n\n  \n    Increases Discoverability: A name that accurately reflects the API’s functionality makes it easier for developers to find the API they need amidst a sea of options.\n  \n  \n    Communicates Value: A strong name hints at the API’s capabilities and potential benefits, piquing the interest of developers and encouraging exploration.\n  \n  \n    Sets Expectations: A clear name sets the stage for what developers can expect from the API, reducing confusion and ensuring alignment with their needs.\n  \n\n\nHere are some tips for enhancing the name field:\n\n\n  Focus on Functionality: Instead of generic names like “Product Service” or “Product API,” use terms that accurately reflect the API’s purpose. For example, “Product Inventory Management” provides a clear understanding of the API’s domain and scope.\n\n\ninfo: \n  title: Product Inventory Management\n  ...\n\n\n\n  \n    Consider Target Audience: Tailor the name to resonate with the intended audience. If the API primarily serves developers, technical terms might be appropriate. However, for broader audiences, simpler and more descriptive language might be preferable.\n  \n  \n    Maintain Consistency: Ensure the name aligns with the overall branding and naming conventions within your organization or project.\n  \n\n\nWords to Avoid in API Names\n\nWhile crafting a clear and concise API name, it’s also important to avoid certain words that can hinder discoverability and understanding:\n\n1. Avoid Internal Implementation Details:\n\n\n  \n    Controller: This term indicates an internal implementation detail of how the API is structured within the code.\n  \n  \n    Service: Similar to “controller,” this refers to an internal component within the API architecture and doesn’t directly convey the functionality to the user.\n  \n  \n    Impl: This abbreviation implies an implementation detail and doesn’t provide any meaningful information about the API’s purpose.\n  \n\n\n2. Avoid Generic Words:\n\n\n  \n    API: While technically accurate, including “API” in the name is redundant and doesn’t add any clarity.\n  \n  \n    System: This is a very broad term and doesn’t provide specific information about the API’s functionalities.\n  \n  \n    Management: This term is vague and could apply to various functionalities within an API.\n  \n\n\n3. Avoid Jargon and Acronyms:\n\n\n  Avoid using industry-specific jargon or acronyms that might not be familiar to all users. This can create confusion and hinder discoverability.\n\n\nRemember, the goal of an API name is to be clear, concise, and accurately reflect the API’s purpose. By avoiding these types of words, you can create a name that is easily understood and discoverable by developers.\n\nEnhancing the description Field: Beyond One-Sentence Summaries\n\nThe description field offers the most significant opportunity to enrich the info section. It should go beyond a single sentence and provide a comprehensive overview of the API’s functionalities, target audience, and key features. Here are some strategies for crafting a compelling description:\n\n\n  \n    Start with the “Why”: Begin by explaining the problem the API solves or the need it addresses. This establishes the API’s value proposition and helps developers understand its potential impact.\n  \n  \n    Highlight Key Features: Briefly outline the API’s core functionalities and the types of operations it supports. This provides a quick overview of the API’s capabilities.\n  \n  \n    Target Audience: Specify the intended audience for the API. This could be developers, internal teams, or external partners. Understanding the target audience helps tailor the description accordingly.\n  \n  \n    Versioning and Changes: If the API is versioned, briefly mention the current version and any significant changes or updates introduced in this version.\n  \n  \n    Additional Resources: Include links to relevant documentation, tutorials, or support channels. This provides developers with additional resources for deeper understanding and assistance.\n  \n\n\nExample of a poor-quality API description:\n\ninfo:\n  title: ACME Customer Management API\n  description: |\n     Manages customers.\n  …\n\n\nExample of a Well-Defined description:\n\ninfo:\n  title: ACME Customer Management API\n  description: |\n    This API provides a comprehensive set of functionalities for managing customer data within the ACME Corporation. Developers can leverage this API to create, retrieve, update, and delete customer information, manage customer orders, and track customer interactions.\n  …\n\n\nLeveraging Tags for Enhanced API Organization\n\nThe OpenAPI specification offers a powerful feature called “tags” that may be used to improve API discoverability and organization. Tags act as logical categories that group related API operations, making it easier for developers to navigate and understand the capabilities offered by the API. The grouping of APIs by tag is something that Bump.sh has found to be a valuable way to increase the understanding of your developers.\n\nThere are two steps to fully take advantage of tags within an OpenAPI specification:\n\n1. Global Tags: Defined at the root level of the specification, global tags provide a centralized overview of all the tags used within the API. This section allows you to:\n\n\n  \n    Define the tag name: Assign a clear and concise name that accurately reflects the category of operations it represents.\n  \n  \n    Add a description: Briefly explain the purpose and functionality associated with the tag.\n  \n  \n    Include external documentation: Link to relevant resources like tutorials or additional documentation specific to the tagged operations.\n  \n\n\n2. Operation-Level Tags: Each individual operation within the paths section of your OpenAPI document can be assigned one or more tags. This allows for granular categorization based on the specific functionality of each operation.\n\nHere’s an example of how tags are defined in the OpenAPI specification for a product catalog API:\n\ntags:\n  - name: products\n    description: Operations related to product management\n  - name: categories\n    description: Operations related to product categories\n  - name: orders\n    description: Operations related to managing product orders\n...\n\npaths:\n  /products:\n    get:\n      summary: Get all products\n      tags:\n        - products\n    post:\n      summary: Create a new product\n      tags:\n        - products\n  /products/{productId}:\n    get:\n      summary: Get a specific product by ID\n      tags:\n        - products\n    put:\n      summary: Update a product\n      tags:\n        - products\n    delete:\n      summary: Delete a product\n      tags:\n        - products\n  /categories:\n    get\n\n\nConclusion\n\nBy investing time in crafting a detailed info section and utilizing tags within your OpenAPI specification, you empower developers with the necessary context and understanding to quickly grasp the API’s purpose, functionalities, and value proposition. This not only streamlines developer onboarding but also fosters a more successful API ecosystem.\n\nFinally, remember that the info section serves as the first impression for developers, so make it count! A well-defined info section, coupled with the use of tags for organization, paves the way for enhanced discoverability and an improved developer experience."
        },
        {
          "id": "guides-technical-writing-5-improvements-to-openapi-operation-documentation",
          "title": "5 Improvements to OpenAPI Operation Documentation",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Technical Writing",
          "tags": "",
          "url": "/guides/technical-writing/5-improvements-to-openapi-operation-documentation/",
          "content": "After conducting API design and documentation reviews for over a decade, there are common mistakes that most developers make when composing their reference documentation using the OpenAPI Specification. Let’s look at five operation-specific improvements that you can make to deliver on a great developer experience while reducing the support costs of your API.\n\n1. Expand Your Operation Descriptions:\n\nOften, API documentation is left to the end of the schedule, resulting in a rushed set of reference documentation. The result are terse operation details that state the obvious and leave out the specifics of your well-designed and implemented API operation.\n\nExample of an Incomplete Operation Documentation\n\nFor example, an operation might be described simply as GET /users with no further explanation, leaving users to guess what the endpoint does.\n\nExample Mistake:\npaths:\n  /users:\n    get:\n      summary: \"Get users\"\n      parameters:\n        - in: query\n          name: limit\n          schema:\n            type: integer\n            default: 10\n      responses:\n        200:\n          description: \"OK\"\n\n\nImproved Version:\nDetailed and clear descriptions enhance understanding and usability. The improved version should thoroughly explain the operation, including its purpose, parameters, and response types.\n\nImproved Example:\npaths:\n  /users:\n    get:\n      summary: \"Retrieves a paginated list of users\"\n      description: &gt;\n        This operation returns a paginated list of users registered in the system, \n        including their usernames and contact information. Use query parameters \n        for filtering and sorting the result set.\n      parameters:\n        - in: query\n          name: limit\n          schema:\n            type: integer\n            default: 10\n          description: \"Limit the number of users returned. Defaults to 10 if not specified\"\n      responses:\n        200:\n          description: \"A paginated list of users with detailed information\"\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/UserList'\n\n\n2. Add Examples to Your API Operations and Schema\n\nEver since OpenAPI v3, examples have been built-in to the specification. OpenAPI examples are used to replace the default values generated by documentation rendering frameworks, such as SwaggerUI or Bump.sh. They offer the API provider the ability to customize the examples generated by these documentation frameworks to make it easier to understand your API operation.\n\nIn addition to better understanding, they also allow teams to document a common use case across operations. The example response of one operation can be used as examples for the input of another, further improving understanding and reducing the time it takes for developers to successfully integrate your API.\n\nExample of missing example values\n\nBelow is an example of API reference documentation that lacks example values.\n\nExample Mistake:\ncomponents:\n  schemas:\n    User:\n      type: object\n      properties:\n        id:\n          type: string\n        name:\n          type: string\n\n\nImproved Version with Example Values:\nAdding examples helps users understand how to interact with the API more effectively. It illustrates what to expect in requests and responses.\n\nImproved Example:\ncomponents:\n  schemas:\n    User:\n      type: object\n      properties:\n        id:\n          type: string\n          example: \"a3a39bcb1f94049d\"\n        name:\n          type: string\n          example: \"John Doe\"\n\n\nBy leveraging values that are synthetic but perhaps more realistic, it helps the developer to better understand what the operation may require in a request or return in a response. Additionally, maintaining consistency between examples help to improve the understanding of how the API operation works.\n\n3. Add Common Response Code Documentation\n\nWith limited time available, API operations may only include documentation for common response codes like 200 OK and 500 Internal Server Error, neglecting other important status codes. Status codes such as 201 Created, 202 Accepted, 400 Bad Request, 401 Unauthorized, and 404 Not Found are also useful to document.\n\nSome API providers have opted to use RFC 9457 Problem Details format (formally RFC 7807) to provide a consistent error message format. By documenting common response codes, not only does it inform the consumer of what to expect when errors occur, it also guides them on the error format of the response.\n\nOf course, any HTTP client must be prepared to handle any response code - even those not documented in the OpenAPI documentation for your API. However, the more response codes you explicitly document, the more likely they will catch and handle those errors within the client code.\n\nExample Operation with Limited Response Codes\n\nExample Mistake:\nresponses:\n  200:\n    description: \"Success\"\n  500:\n    description: \"Server Error\"\n\n\nImproved Version with Common Error Response Codes\n\nImproved Example:\nresponses:\n  200:\n    description: \"Success\"\n    …\n  400:\n    description: \"Bad Request - Invalid parameters\"\n    content:\n      application/json:\n        schema:\n          $ref: '#/components/schemas/ProblemDetails'\n  401:\n    description: \"Unauthorized - Authentication failed\"\n    content:\n      application/json:\n        schema:\n          $ref: '#/components/schemas/ProblemDetails'\n  404:\n    description: \"Not Found - Resource doesn’t exist\"\n    content:\n      application/json:\n        schema:\n          $ref: '#/components/schemas/ProblemDetails'\n  500:\n    description: \"Internal Server Error - System encountered an unexpected error\"\n    content:\n      application/json:\n        schema:\n          $ref: '#/components/schemas/ProblemDetails'\n\n\n4. Organize and Tag Your API Operations\n\nWhile most developers will never look at the underlying YAML for your reference documentation, most documentation rendering frameworks will render operations in the order that they are captured. This means that related operations that are scattered across your OpenAPI document will make it more difficult for the developer to understand how to use your API and what capabilities it offers.\n\nAt a minimum, group your operations so that they render together and in a thoughtful manner that helps with understanding. You may also wish to use tags to group related operations together. Tags are simply a grouping mechanism that helps documentation frameworks organize the display of your API operations.\n\nExample of Disorganized API Operation Documentation\n\nDocumentation that lacks structure, such as grouping related endpoints or models, can be challenging to navigate and understand.\n\nExample Mistake:\npaths:\n  /users:\n    get: ...\n  /orders:\n    get: ...\n  /users/{id}:\n    get: ...\n\n\nWell-Organized and Tagged API Operations\n\nImproved Example:\ntags:\n  - name: User Management\n    description: Manages the registered users in the system\n  - name: Order Management\n    description: Supports order creation and status lookup\npaths:\n  /users:\n    get: ...\n      tags:\n        - User Management\n\n  /users/{id}:\n    get: ...\n      tags:\n        - User Management\n  /orders:\n    get: ...\n      tags:\n        - Order Management\n\n\nRefer to our guide, “Using OpenAPI and AsyncAPI Tags to Better Organize API Endpoints” for further details on using tagging in your OpenAPI description.\n\n5. Improve Consistency in Operation Naming Conventions\n\nInconsistent naming conventions for operations, parameters, and schema properties can lead to confusion and errors in API integration. Since making changes to these conventions can break your consumers, it is important to take a fresh look at your API operations, parameters, and schema definitions before releasing your API.\n\nTo further improve your API design efforts, involve your technical writers earlier in the API design process. This will provide insights regarding improvements that could be made to improve the developer experience. By shifting your technical writer involvement earlier in the API lifecycle, you also reduce the cost of change due to late feedback.\n\nAn Example of Inconsistent or Unclear Naming\n\nBelow is an example of API operations that don’t follow a consistent naming convention, resulting in an increased likelihood of mistakes when integrating your API.\n\nExample Mistake:\npaths:\n  /calcSalesTax:\n    post: ...\n  /vatCalculation:\n    post: ...\ncomponents:\n  schemas:\n    CalculationResponse:\n      type: object\n      properties:\n        amount:\n          type: number\n          format: double\n        …\n\n\nImproved Version:\nUsing a consistent, predictable naming convention improves readability and usability of the API documentation.\n\nImproved Example:\npaths:\n  /calculateSalesTax:\n    post: ...\n  /calculateVatTax:\n    post: ...\ncomponents:\n  schemas:\n    CalculatedTax:\n      type: object\n      properties:\n        amount:\n          type: number\n          format: double\n        currency:\n          type: String\n        …\n\nBonus: Applying OpenAPI Specification Overlays\n\nFor teams that are generating OpenAPI Specification documents directly from their code using annotations, technical writers can further benefit from the use of OpenAPI overlays. Refer to our article that details using overlays to improve your tech writing workflow.\n\nFinal Thoughts on Improving Your API Documentation\n\nCreating a high-quality API document requires attention to detail, thoroughness, and a focus on the user’s experience. Take the additional time to review your documentation, look for areas to improve, and identify design flaws early so that they can be corrected with minimal impact to developers. Doing so will produce clear, comprehensive, and usable API documentation that results in a better developer experience."
        },
        {
          "id": "guides-bump-sh-tutorials-try-requests-in-postman",
          "title": "Try HTTP Requests with Postman",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Bump-sh tutorials",
          "tags": "",
          "url": "/guides/bump-sh-tutorials/try-requests-in-postman/",
          "content": "Bump.sh now includes its own API Explorer, allowing you to test your API directly within your documentation. Give it a try!\n\n\nSeeing how an API works is the first step in an end-users journey to using the API, and the second step is making some test requests to get a feel for how it works. Some people like to do this with code, so code samples will be a good start for them, especially if you have an SDK. Other people like to do this with interactive HTTP clients, like Postman.\n\nPostman is one of the original modern API consoles. Users can create or import “collections” which are very similar to OpenAPI but focus more on actual requests and responses being saved, like a whole bunch of bookmarks in a browser. Those collections can be distributed in various ways, and it’s becoming increasingly common for API providers to publish a read-only public Postman Collection for end-users to download, so they have a whole interactive API console ready to go.\n\nThis guide will go through getting started with Postman, creating a “Postman Collection” from the same OpenAPI that powers your documentation, to finally getting a “Run in Postman” button into your Bump.sh API docs.\n\nStep 1) Install Postman\n\nDownload and install Postman on your computer and load it up.\n\nStep 2) Create a Postman account\n\nCreate a new account (it’s free). If you’re doing this for a business or organization other than yourself you can create a workspace your collections to live in.\n\n\n\nStep 3) Connect your API to Git version control\n\nOn the left sidebar click APIs, and you should see the option to create a new API. If this option is missing open up Workspace settings and configure the sidebar to add the APIs option.\n\nThe APIs interface will show you have no APIs, so click “Create” and give the API a name.\n\n\n\nThen you can configure which Git repository your API should be connected to.\n\n\n\nNext, point Postman to the “API schema file”, which is their way of saying “API description document.” Whatever you call it, this is the same openapi.yaml document that you use to deploy to Bump.sh.\n\nIt’s possibly sat in the root of your repository, but if you’re not sure, you can look at your .github/workflows/bump.yml and see where the deploy command is looking for the file: {filename}.\n\nClick “Connect Repository” when it’s all done and you’ll be taken to the API overview page.\n\nStep 4) Setup Postman in the Repository\n\nBefore you can publish this API to the world, we need to help Postman identify the specific API being described. If you hover over the Publish button it will suggest some changes, which it can commit for you.\n\n\n\nClick the commit button and then create a pull request, ideally through the Postman “Create pull request” button on the same screen. It will take you to your Git hosting provider on the pull request page. The pull request contains a few bits of config, like the Postman API ID.\n\nIf you’d like to see what it looks like, take a look at the Train Travel API pull request, or jump right in with your API.\n\nWhen this is done, Postman will be all set up to work with your API in your repository.\n\nStep 5) Generate a Postman Collection\n\nPostman is now aware of your OpenAPI document, but in order to share a collection you’ll need to generate a collection from your OpenAPI.\n\n\n\nClicking “Generate from definition” you’ll see a modal window with a whole bunch of options. To start off just keep them all default, perhaps changing the Parameter Generation setting to Example if your OpenAPI happens to have great examples in there already.\n\n\n\nAlmost done! The collection lives in the APIs panel under our API, but in order to use the full power of Postman we need to copy it over to the main Collections tab.\n\n\n\nStep 6) Integrate the Run in Postman button to Bump.sh\n\nFinally, this collection is ready to rock, and we can embed the Run in Postman button into our Bump.sh documentation.\n\nTo get the button, click on the collection and find the Share option, then select the Mxarkdown version.\n\n\n\nTake that Markdown, and pop it into your OpenAPI somewhere. The easiest place to put it is right in the info.description property.\n\nopenapi: 3.1.0\ninfo:\n  title: Train Travel API\n  description: |\n    API for finding and booking train trips across Europe.\n\n    ## Run in Postman\n\n    Experiment with this API on Postman, using our Postman Collection.\n    \n    [&lt;img src=\"https://run.pstmn.io/button.svg\" alt=\"Run In Postman\" style=\"width: 128px; height: 32px;\"&gt;](https://app.getpostman.com/run-collection/9265903-7a75a0d0-b108-4436-ba54-c6139698dc08?action=collection%2Ffork&amp;source=rip_markdown&amp;collection-url=entityId%3D9265903-7a75a0d0-b108-4436-ba54-c6139698dc08%26entityType%3Dcollection%26workspaceId%3Df507f69d-9564-419c-89a2-cb8e4c8c7b8f)\n\n\n\n  A better place for this button might be a dedicated Topic, using the x-topics extension supported by Bump.sh documentation.\n\n\nTo see how this looks in Bump.sh before pushing it live, you can use the Bump.sh CLI’s preview command.\n\nnpx bump-cli preview openapi.yaml --live\n\n\nThe command will return a link, click that and see how the documentation looks.\n\n\n\nWhen you (or your users) click the new Run in Postman button, they’ll be able to view the original, or fork the collection to their own workspaces to customize the requests for their own needs. When it loads up, the collection will show all the possible requests, and help them add the right headers, parameters, and authorization details."
        },
        {
          "id": "guides-technical-writing-api-documentation-checklist",
          "title": "API Documentation Checklist",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Technical Writing",
          "tags": "",
          "url": "/guides/technical-writing/api-documentation-checklist/",
          "content": "Your API is nearing completion and it’s time to let the world know about it. This means that it is time to complete your API documentation effort. But, where should you start? How do you know if you covered everything that your decision makers and developers will need to select your API and get started successfully?\n\nThis article provides a checklist to help you identify the documentation you will need for launching your API. We will also include some things to consider post-launch as well to help you continue to improve your documentation.\n\nLaunch Day API Documentation Checklist\n\nAs the saying goes, “you only get one chance to make a first impression”, which means that your API documentation must be complete and answer all of the questions of your prospective customer. This includes a high-level overview of your API, how to get started, and reference documentation. By addressing these concerns, you help both decision makers and developers to get started with little friction.\n\n1. Overview and Introduction\n\n  Describe the API’s purpose and capabilities.\n  Provide an overview of the main features and benefits.\n  If your API has been around for a while, include any version information and update history.\n\n\n2. Authentication and Authorization Guide\n\n  Explain the authentication methods supported (e.g., OAuth 2.0, API keys).\n  Step-by-step guide on how to authenticate.\n  Information on obtaining, refreshing, and managing tokens or keys.\n  Any scopes or permissions required for different API endpoints.\n\n\n3. Getting Started Guide\n\n  Installation instructions (if this is a self-hosted API).\n  Initial setup or configuration steps that may be required before using your API.\n  Quickstart guide with a simple but complete example demonstrating basic use. A GET-based operation with minimal request details is best.\n\n\n4. Reference Documentation (OpenAPI Specification)\n\n  Detailed description of each endpoint, including path, method, and description.\n  Parameters for each endpoint, including names, locations (path, query, header, body), types, and descriptions.\n  Request examples for each endpoint.\n  Response models and examples, including status codes and potential error messages.\n  Authentication requirements for each endpoint.\n\n\n5. Error Handling\n\n  Describe common errors, their causes, and how to resolve them.\n  Include error response formats and status codes.\n\n\n6. Rate Limiting and Quotas\n\n  Explain any rate limits, quotas, or usage policies.\n  Describe how to check usage and what happens when limits are exceeded.\n\n\n7. Versioning and Deprecation Policy\n\n  Document the current version and how versioning is handled, even if this is your first version, to build consumer trust.\n  Provide information on deprecation policy and end-of-life dates for older versions.\n\n\n8. Support and Community\n\n  Contact information for API support, including email, chat, and/or a ticketing system.\n  Links to community forums, Q&amp;A sites, or social media groups.\n\n\n9. Feedback and Contribution\n\n  Instructions on how to provide feedback or report bugs.\n  Guidelines for contributing to the API documentation, including errata or improvements.\n\n\n10. Legal and Compliance\n\n  Terms of service, privacy policy, and any legal notices.\n  Compliance with standards or regulations relevant to the API’s domain.\n\n\nDay 2 API Documentation Improvements\n\nCongrats - you launched your API! But just because you have launched your API doesn’t mean your documentation efforts are done. Instead, look for ways to improve your documentation based on feedback from your developer community. Keep your documentation updated with upcoming community events, SDKs and libraries that can accelerate the development effort, and examples, tutorials, and reference applications to show what is possible with your API.\n\n11. Community Events\n\n  Information about official or community-driven events, meetups, and webinars.\n  Upcoming conferences you are sponsoring or attending\n  Videos from recent webinars and events.\n\n\n12. SDKs and Libraries\n\n  List available SDKs or libraries for different programming languages, including those that are coming soon.\n  Include installation instructions, documentation, and examples for each SDK.\n  Links to one or more code repositories where the SDK source code is made available and pull requests can be issued for improvements or fixes.\n\n\n13. Examples and Tutorials\n\n  Code examples in multiple languages or frameworks if applicable.\n  Step-by-step tutorials for common workflows or use cases.\n  Link to Postman collection or similar tool for API exploration and testing.\n\n\n\n  Bump.sh now includes its own API Explorer, allowing you to test your API directly within your documentation. Give it a try!\n\n\n14. Reference Applications\n\n  Provide sample applications or integrations that use the API.\n  Include source code, setup instructions, and usage guide, preferably hosted on GitHub, GitLab, or equivalent to support community contributions.\n  Discuss common use cases addressed by the reference applications.\n\n\n15. Industry Use Cases and Case Studies\n\n  Expand your marketing and documentation efforts to include insights for specific industries or personas that could benefit from your API.\n  Document case studies about how your API solved problems for a specific industry.\n\n\nAdditional API Documentation Improvements\n\nAfter some time, your documentation can become stale. Consider taking these additional steps to freshen up your content:\n\n\n  Add a changelog to list API enhancements and fixes to help your customers understand what has changed since the last time they viewed your documentation.\n  Review your documentation to ensure you have standardized on terminology. This will ensure you always use the same terms to mean the same thing throughout the docs.\n  Create a shared product roadmap to help current and future customers know where you are heading with the next release.\n  Update text to include terms users are likely to search for to help optimize organic search results.\n  Reorganize your documentation to improve the logical order of sections and content to make things easier to find.\n  Add business-focused content for nontechnical or less-technical users and decision makers.\n  Extend code examples into complete tutorials that explain each step in detail.\n\n\nFinal Thoughts\n\nHaving a comprehensive and well-structured documentation set, as outlined above, will significantly enhance the developer experience and facilitate easier integration with your API. If you are not sure how to get started producing great documentation, consider checking out Bump.sh. You may also want to visit the minimum viable portal (MVP) project from LaunchAny to help you get started quickly with a full developer portal."
        },
        {
          "id": "guides-openapi-jsonpath",
          "title": "How to use JSONPath",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "OpenAPI",
          "tags": "",
          "url": "/guides/openapi/jsonpath/",
          "content": "A few years ago most API designers, developers, and technical writers would have had very little reason to bump into JSONPath, but its starting to get more and more relevant as more tools and standards start relying on it. So what is JSONPath, what is it used for, and how can you get up to speed with using it?\n\nJSONPath is a query language that can be used to extract data from JSON documents, which at first might not sound very exciting, but remember… OpenAPI is just a JSON (or YAML) document, so you can use JSONPath to poke around in OpenAPI and do various things.\n\nYou can use JSONPath for OpenAPI Overlays, to patch OpenAPI documents with extra documentation content, code samples, or whatever else.\n\nYou can use JSONPath in Spectral to write incredibly advanced linting rules which can power your automated API Style Guides.\n\nYou can even use JSONPath in AWS Step Functions.\n\nJSONPath is popping up all over the the place these days, and if you work with OpenAPI it’s definitely a handy tool to have on your belt.\n\nHow does JSONPath Work?\n\nJSONPath is one of several query languages which will let you filter, query, and traverse through a chunk of JSON, not just to pull bits out, but to navigate complex data structures, with syntax for getting into specific array indexes, filtering through an objects properties or array values before continuing on to its children.\n\nHere’s a sample JSONPath from the RFC.\n\n$.store.book[?@.price &lt; 10].title\n\n\n\n  Anyone familiar with XPath in XML will be thinking “hmm, this looks pretty familiar!” and you’re spot on, JSONPath is inspired by XPath. If you’ve never heard of XPath no worries, we’ll start from scratch here.\n\n\nTo see how this works we’ll need some JSON to run it against, so here is an example of some JSON from the RFC.\n\n{\n  \"store\": {\n    \"book\": [\n      {\n        \"category\": \"reference\",\n        \"author\": \"Nigel Rees\",\n        \"title\": \"Sayings of the Century\",\n        \"price\": 8.95\n      },\n      {\n        \"category\": \"fiction\",\n        \"author\": \"Evelyn Waugh\",\n        \"title\": \"Sword of Honour\",\n        \"price\": 12.99\n      },\n      {\n        \"category\": \"fiction\",\n        \"author\": \"Herman Melville\",\n        \"title\": \"Moby Dick\",\n        \"isbn\": \"0-553-21311-3\",\n        \"price\": 8.99\n      },\n      {\n        \"category\": \"fiction\",\n        \"author\": \"J. R. R. Tolkien\",\n        \"title\": \"The Lord of the Rings\",\n        \"isbn\": \"0-395-19395-8\",\n        \"price\": 22.99\n      }\n    ],\n    \"bicycle\": {\n      \"color\": \"red\",\n      \"price\": 399\n    }\n  }\n}\n\n\nNow when you run that through any sort of JSONPath tool, you could expect to see these results.\n\n[\n  \"Sayings of the Century\",\n  \"Moby Dick\"\n]\n\n\nSyntax\n\nThere is a whole lot of syntax to learn, but once you figure out the constituent pieces you can start to compile them into really advanced queries.\n\n\n  \n    \n      Syntax\n      Element Description\n    \n  \n  \n    \n      $\n      root node identifier (Section 2.2)\n    \n    \n      @\n      current node identifier (Section 2.3.5) (valid only within filter selectors)\n    \n    \n      [&lt;selectors&gt;]\n      child segment (Section 2.5.1): selects zero or more children of a node\n    \n    \n      .name\n      shorthand for [‘name’ ]\n    \n    \n      .*\n      shorthand for [*]\n    \n    \n      ..⁠[&lt;selectors&gt;]\n      descendant segment (Section 2.5.2): selects zero or more descendants of a node\n    \n    \n      ..name\n      shorthand for .. [’ name’ ]\n    \n    \n      ..*\n      shorthand for ..[*]\n    \n    \n      'name'\n      name selector (Section 2.3.1): selects a named child of an object\n    \n    \n      *\n      wildcard selector (Section 2.3.2): selects all children of a node\n    \n    \n      3\n      index selector (Section 2.3.3): selects an indexed child of an array (from 0)\n    \n    \n      0:100:5\n      array slice selector (Section 2.3.4): start:end:step for arrays\n    \n    \n      ?&lt;logical-expr&gt;\n      filter selector (Section 2.3.5): selects particular children using a logical expression\n    \n    \n      length(@.foo)\n      function extension (Section 2.4): invokes a function in a filter expression\n    \n  \n\n\nOverview of JSONPath Syntax, from RFC 9535.\n\nExamples\n\nIf that isn’t making too much sense, here are some examples to help you visualize.\n\n\n  \n    \n      JSONPath\n      Intended Result\n    \n  \n  \n    \n      $.store.book[*].author\n      the authors of all books in the store\n    \n    \n      $..author\n      all authors\n    \n    \n      $.store.*\n      all things in the store, which are some books and a red bicycle\n    \n    \n      $.store..price\n      the prices of everything in the store\n    \n    \n      $..book[2]\n      the third book\n    \n    \n      $..book[2].author\n      the third book’s author\n    \n    \n      $..book[2].publisher\n      empty result: the third book does not have a “publisher” member\n    \n    \n      $..book[-1]\n      the last book in order\n    \n    \n      $..book[0,1]\n      the first two books\n    \n    \n      $..book[:2]\n      the first two books\n    \n    \n      $..book[?@.isbn]\n      all books with an ISBN number\n    \n    \n      $..book[?@.price&lt;10]\n      all books cheaper than 10\n    \n    \n      $..*\n      all member values and array elements contained in the input value\n    \n  \n\n\nExample JSONPath Expressions and Their Intended Results When Applied to the Example JSON Value, from RFC 9535: 1.5. JSONPath Examples.\n\nBy combining these bits of example syntax together you can do amazing and powerful things with JSONPath, so let’s look at how to do those amazing things in OpenAPI.\n\nJSONPath &amp; OpenAPI\n\nTake an OpenAPI document, like the Train Travel API.\n\ngit clone github.com/bump-sh-examples/train-travel-api\n\ncd train-travel-api\n\n\nThen install jsonpath-cli just so we can try some things out.\n\nnpm install -g @jsware/jsonpath-cli\n\n\nOptional, if you’re working with YAML, you might want to convert from YAML to JSON in the CLI too.\n\nbrew install yq\n\nyq eval -o=json openapi.yaml &gt; openapi.json\n\n\nDon’t worry this is just for playing around, all of the tooling that uses JSONPath will support YAML without bodges like this. Let’s just get on the same page for this guide.\n\nQuerying OpenAPI with JSONPath\n\nOnce you have a JSON file to work with, we can use the jpp command, pass in a JSON/YAML document, and provide a JSONPath expression to query the document for specific parts.\n\n$ jpp --pretty '$.info' openapi.json\n\n[\n  {\n    \"title\": \"Train Travel API\",\n    \"description\": \"API for finding and booking train trips across Europe.\",\n    \"version\": \"1.0.0\",\n    \"contact\": {\n      \"name\": \"Train Support\",\n      \"url\": \"https://example.com/support\",\n      \"email\": \"support@example.com\"\n    },\n    \"license\": {\n      \"name\": \"Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International\",\n      \"identifier\": \"CC-BY-NC-SA-4.0\"\n    }\n  }\n]\n\n\nIn this example $ refers to the root JSON document, then .info is using dot notation to access the info key in that object.\n\nWe can get a bit more advanced, and pull up a list of paths.\n\n$ jpp --pretty '$.paths.*~' openapi.json\n\n[\n  \"/stations\",\n  \"/trips\",\n  \"/bookings\",\n  \"/bookings/{bookingId}\",\n  \"/bookings/{bookingId}/payment\"\n]\n\n\nThis uses the .* syntax which is basically grabbing all children of the paths object, then using ~ to grab the keys instead of the values.\n\nWhat sort of query language would JSONPath be if we could not do queries? Let’s pull up a list of paths which are a get or a post, but ignore all the put, patch, delete, etc.\n\n$ jpp --pretty '$.paths[?(@.put || @.post)]~' openapi.json\n\n[\n  \"/bookings\",\n  \"/bookings/{bookingId}/payment\"\n]\n\n\nOpenAPI Overlays powered by JSONPath\n\nOne of the main uses for JSONPath will be for working OpenAPI documents, often by technical writers or other folks in the API governance space to check or improve OpenAPI documents.\n\nOverlays are a list of actions, which make up a “target” which is a JSONPath, and an operation of either “update” or “remove”.\n\nLet’s look at an update command.\n\n# overlays.yaml \n\noverlay: 1.0.0\ninfo:\n  title: Overlay to customise API for Protect Earth\n  version: 0.0.1\nactions:\n  - target: '$.info'\n    description: Update description and contact for our audience.\n    update:\n      description: &gt;\n        A new and much more interesting long form description, which has all sorts of \n        Markdown, or more specifically [CommonMark](https://commonmark.org/) which \n        is _like_ Markdown but **better**, because it's an actual standard instead of a \n        series of sometimes vaguely consistent conventions.\n\n        Anyway, this is a good place to write all sorts of helpful stuff, link to other\n        getting started content, link to where people can find access tokens, or even\n        paste some code samples for getting your first API request off the ground.\n\n      contact:\n        name: Support Team\n        url: https://example.com/contact\n        email: support@example.org\n\n\n\nThis overlays file is pointing to the JSONPath target $.info, then updating the object with the new bits of OpenAPI for description and contact, as per the OpenAPI specification. This can be handy for improving the quality of all sorts of descriptions, not just info, and for popping in support team contact information if the API developers inevitably forgot to mention that sort of thing.\n\nInstead of using those yq or jpp tools we grabbed just to practice, we can use the Bump.sh CLI which has support for Overlays built in, and thankfully it’ll work just fine with YAML or JSON.\n\nnpm install -g bump-cli\n\nbump overlay openapi.yaml overlays.yaml &gt; openapi.new.yaml\n\n\nIf we were to run that overlay on the Train Travel API, the resulting openapi.new.yaml would like like this:\n\nopenapi: 3.1.0\ninfo:\n  title: Train Travel API\n  description: &gt;\n    A new and much more interesting long form description, which has all sorts\n    of  Markdown, or more specifically [CommonMark](https://commonmark.org/)\n    which  is _like_ Markdown but **better**, because its an actual standard\n    instead of a  series of sometimes vaguely consistent conventions.\n\n    Anyway, this is a good place to write all sorts of helpful stuff, link to\n    other getting started content, link to where people can find access tokens,\n    or even paste some code samples for getting your first API request off the\n    ground.\n  version: 1.0.0\n  contact:\n    name: Support Team\n    url: 'https://example.com/contact'\n    email: support@example.org\n  license:\n    name: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International\n    identifier: CC-BY-NC-SA-4.0\n# snipped\n\n\nWhen combined with more advanced queries you can start to get really specific with bits of the OpenAPI document you’d like to update, enabling all sorts of random use cases like cleaning up the servers list for publishing an API Catalogue, removing Development and Staging servers not accessible or relevant to API consumers.\n\n# openapi.yaml \nopenapi: 3.1.0\nservers:\n  - url: http://localhost:3000\n    description: Development\n\n  - url: https://api-staging.example.com\n    description: Staging\n\n  - url: https://api.example.com\n    description: Production\n\n\nAn overlay can target the servers array with $.servers then query through them with $.servers[?(@.description==\"Development\" || @.description==\"Staging\")], which is looking through objects in the array, and looking through the children for description: Development or description: Staging using basically JavaScript syntax.\n\nThe Overlay for this would combine that JSONPath target with remove: true operation like this:\n\n# overlays.yaml\noverlay: 1.0.0\ninfo:\n  title: Overlay to customise API\n  version: 0.0.1\nactions:\n  - target: '$.servers[?(@.description==\"Development\" || @.description==\"Staging\")]'\n    description: Remove Development and Staging servers but leave anything else.\n    remove: true\n\n\nThat would leave this resulting OpenAPI.\n\n# openapi.yaml \nopenapi: 3.1.0\nservers:\n  - url: https://api.example.com\n    description: Production\n\n\nThen the Developer Experience folks decide to roll out a Mocking or Sandbox experience, where consumers can play around with requests without actually triggering real emails, real data, or spending real money, but how can we show everyone where that is? Do we have to go and pester all the API teams to add it? Nope, just add another action.\n\n# overlays.yaml\noverlay: 1.0.0\ninfo:\n  title: Overlay to customise API\n  version: 0.0.1\nactions:\n  - target: '$.servers[?(@.description==\"Development\" || @.description==\"Staging\")]'\n    description: Remove Development and Staging servers but leave anything else.\n    remove: true\n\n  - target: '$.servers'\n    description: Let everyone know about our amazing new hosted mocking/sandbox server.\n    update:\n      - description: Sandbox\n        url: https://api-sandbox.example.com/\n\n\nLeaning more about JSONPath\n\nJSONPath made it to IETF “proposed standard” RFC status in 2024 (RFC 9535), but before then it was in a similar position to Markdown in the days before CommonMark, in that there are a few different variations of JSONPath as a concept.\n\n\n  JSONPath “The Blog Post” - Written by Stefan Gössner in 2007.\n  jsonpath.com - An online evaluator which as far as I can tell matches the blog post.\n  JSONPath-Plus - A popular (but now abandoned) fork which expands on the original specification to add some additional operators.\n  Nimma - A fork of JSONPath Plus created by the Stoplight team for Spectral to handle more advanced use cases. A list of caveats can be found here.\n\n\nThen to further compound this confusion, all of the implementations have different support for certain features, and have filled in the grey areas differently due to their own interpretations and community requests. The amazing JSONPath Comparison project has collated all of the differences into a massive test suite and published the results, which was really helpful in shaping the new standard. Hopefully this will help tools converge, and we can forget all about this incompatibility.\n\nFor now, try to follow the RFC 9535 syntax, and use tooling which lines up with that syntax. Unfortunately that means not using jsonpath.com, and even the jpp CLI tool we used earlier is JSONPath Plus, which has a few differences to the RFC…\n\nThe Bump.sh CLI overlays functionality is JSONPath RFC 9535 compliant, and if you spot any valid RFC JSONPath syntax not working as expected please create an issue on GitHub so we can get that sorted out."
        },
        {
          "id": "guides-technical-writing-elements-of-great-api-documentation",
          "title": "The Elements of Great API Documentation",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Technical Writing",
          "tags": "",
          "url": "/guides/technical-writing/elements-of-great-api-documentation/",
          "content": "When you think of API documentation, what comes to mind? More than likely, you think of reference documentation that describes how to use an API, including the operations and request/response details. This kind of reference documentation leverages the OpenAPI Specification (formerly known as Swagger). However, there is more to documenting your API than just the reference documentation. Let’s explore the different types of API documentation and the role each has to play in helping a developer become proficient in consuming your API.\n\nAPI documentation is the user interface for developers\n\nAPI documentation is the most important user interface for developers who will integrate your API. It acts as the primary communication medium between the API producer and the many developers who will consume the API via applications and automation scripts.\n\nYet, API documentation is often the least-favorite step for most developers. Within an organization, API consumers may have access to the source code to better understand how the API works. So documentation is often left off, citing access to the source code as the best way to understand how to use an API.\n\nBut even if access to the source code is possible, reading code to understand an API is unacceptable. It slows down the developer. At best, it causes frustration. At worst, it results in the developer building the API themselves rather than just consuming yours.\n\nInvesting in proper documentation is very important for your API. Going beyond the reference documentation of your API operations to other types of documentation can unlock increased value for your API consumers.\n\nAPI reference documentation\n\nTraditionally, API reference documentation was locked in PDF or Microsoft Word documents that started to grow stale the moment they were downloaded to a developer’s laptop. API description formats, such as the OpenAPI Specification and more recently TypeSpec, capture API reference documentation in machine-readable format. RPC and REST-based APIs leverage these formats to capture the details of their API operations.\n\nOnce captured in these formats, tools are used to convert the machine-readable format into HTML documentation, generate client-side libraries, and produce a skeleton of server-side code with common patterns and practices already established. Some formats even support API interaction right from the generated API reference documentation. Bump.sh is a great option for teams looking to produce professional API documentation.\n\nGraphQL-based APIs leverage the schema definition language (SDL) to capture the query and mutation operation details. The SDL documents are then used to generate documentation and playground documentation, allowing developers to explore the GraphQL-based API.\n\nLikewise, gRPC uses the interface definition language (IDL) to capture the service, operations, and input/output fields for the API. Reference documentation and code is then generated for clients in their preferred language to understand and consume the API.\n\nReference documentation is a foundational element for documenting your API. But the documentation effort doesn’t stop there. Let’s explore other documentation types that can enhance the developer experience.\n\nCode examples are documentation, too\n\nYou may not realize it, but code examples are also a form of API documentation. They demonstrate how to use the API in practice, either using HTTP request/response interactions or perhaps by using a client helper library in the developer’s native programming language.\n\nCode examples come in a variety of forms, from just a few lines that demonstrate how a specific operation works to reference applications.\n\nTime to First Hello World, or TTFHW, has been a key metric for API owners to determine how long it takes for a developer to start consuming an API. While API products may measure this time in minutes, some APIs may require hours or days to successfully get started with your API.\n\nThe use of code examples as part of your API documentation effort helps to accelerate the time it takes to start using your API. The best code examples require nothing more than dropping in their API authorization token into the example before building and executing the example.\n\nCode examples are a great documentation technique to help jumpstart developers. But sometimes a code example isn’t enough. Instead, it takes a getting started guide to help them kickstart their experience.\n\nGetting started guides drive the developer’s initial experience\n\nAPIs rarely gain adoption if it is difficult to get started. Provide a getting started guide to introduce developers to common use cases that the API solves and provides a step-by- step guide to getting started for each case. Sometimes these guides are referred to as a quick start guide.\n\nGetting started guides should help the developer through the following milestones:\n\n\n  Registering an application using the developer portal\n  Steps to generate an authorization token\n  Making the first API call, preferably a simple read-only operation\n  A guided tour of the API’s other capabilities, from common operations to error handling and pagination\n  How to use refresh tokens to update an expired authorization token\n\n\nHelping to bootstrap the developer experience with a getting started guide can help consumers jump in quickly and start solving problems with your API.\n\nFor APIs that are designed to handle more complex interactions, your API documentation may require several guides. Sometimes these are called ‘cookbooks’ or ‘implementation guides’, as they help developers to understand how to solve a specific problem. You may need to produce additional guides to address specific use cases based upon the needs of a target persona, market segment, or business process.\n\nCross-promoting these guides is an important step. Offer the guides directly from your developer portal, link to them from reference documentation, and list them at the end of your getting started guide. Making them available from multiple areas of your documentation and support processes will help to meet developers where they are and address their needs.\n\nChange logs keep developers up-to-date\n\nA final element to incorporate into your documentation is a change log or release notes. While you may have spent days, weeks, or perhaps even months discovering, designing, and delivering new capabilities for your APIs, your consumers were busy solving their own problems. Use release notes to keep developers informed on the latest updates to your API, including new operations, bug fixes, and new API product offerings.\n\nOne of the challenges of managing change logs and release notes is that it is easy to forget a small change you made that can have a large impact on your developers. Use Bump.sh to quickly spot changes between API revisions and capture those in your release notes.\n\nWrap-up\nThe creation of comprehensive API documentation is critical for the success and usability of any API. It extends beyond mere reference documentation to include a variety of resources that enhance the developer experience, such as code examples, getting started guides, and release notes. These elements together form a robust user interface for developers, facilitating easier integration and quicker adoption of the API.\n\nIf your team is struggling to produce documentation for your API, remember that great API documentation is not just about providing information but about empowering developers to effectively and confidently use the API to its full potential."
        },
        {
          "id": "guides-openapi-openapi-moonwalk",
          "title": "OpenAPI v4.0 (A.K.A \"Project Moonwalk\")",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "OpenAPI",
          "tags": "",
          "url": "/guides/openapi/openapi-moonwalk/",
          "content": "What is coming next for OpenAPI, as v4.0 of the OpenAPI Specification gets closer to being released? What major changes are coming, how easy will it be to upgrade, and how do tooling companies feel about it?\n\nRecent History of OpenAPI\n\nOpenAPI has been around for a long time, but only hit the mainstream when OpenAPI v3.0 was released in 2017. It made it a whole lot easier to describe the majority of REST/RESTish APIs, and brought in major investment from tooling developers big and small who jumped at the chance to add value to the community.\n\nOpenAPI v3.1 came out in early 2021, and focused on fixing issues that appeared with this influx of new users in v3.0. A large part of the focus was solving the subtle but problematic differences between JSON Schema and the OpenAPI Schema Object. These two specifications looked similar enough that many users (and even tooling) treated them as interchangeable, but OpenAPI Schema Objects were a subset and a superset of JSON Schema, which caused confusion for years. OpenAPI v3.1 aligned Schema Objects to be a valid “dialect” of JSON Schema 2020-12, which not only solved this long standing issue, but brought lots of useful new keywords like if/then/else to replace awkward nested allOf &gt; oneOf usage. Other useful functionality like support for Webhooks expanded the type of APIs that could be described.\n\nSo what is OpenAPI v4.0 about?\n\nIntroducing OpenAPI Project Moonwalk\n\nWith OpenAPI v3.1 released and chugging along happily, the OpenAPI TSC spent about a year keeping an eye on feedback, patching the specification with fixes and clarifications, then started collating ideas for the next version. Would it be 3.2? Would it be 4.0? Both?\n\nIt wasn’t entirely clear for a while which would be the next step, but in late 2022 the OpenAPI Technical Steering Committee set up a Special Interest Group to start hashing out what the community needed out of a new major OpenAPI version, and that resulted in Project Moonwalk, a repository where ideas could initially be hashed out and potentially abandoned without confusing everyone with OpenAPI v4.0 being “cancelled” and forcing them to skip a version like PHP’s missing version 6… 🤣\n\nThe primary goal of Project Moonwalk is to make the next version of OpenAPI more approachable, and less complex for both humans and computers to write and understand.\n\nReduce nested structures to improve readability and editability\n\nLet’s be honest, OpenAPI v3 can sometimes feel like a confusing pile of YAML. It’s grown to add excellent functionality which is a large part of how it achieved dominance against earlier API Description Formats, but it’s got pretty complex to write by hand. This complexity has lead to a plethora of open-source and paid tooling aiming to assist the editing experience, from DSL’s to write OpenAPI in easier language, to visual editors to avoid ever needing to look at the YAML.\n\nMore recently LLMs like Copilot are helping remind users, myself included, how to write out all the right response, mime type, content, status code, schema, keywords… when core contributors of OpenAPI need to melt the ice caps with AI just to remember what keyword comes next, it might be time for a simplification.\n\nHere’s the new structure (as it stands).\n\n\n\nDiagrams like this can fail to convey much meaning to folks like myself, so lets create an example before (OAS3) and after (OAS4) by taking a snippet from the Train Travel API (source on GitHub).\n\n/bookings/{bookingId}:\n  parameters:\n    - name: bookingId\n      in: path\n      required: true\n      description: The ID of the booking to retrieve.\n      schema:\n        type: string\n        format: uuid\n      example: 1725ff48-ab45-4bb5-9d02-88745177dedb\n  get:\n    summary: Get a booking\n    description: Returns the details of a specific booking.\n    operationId: get-booking\n    responses:\n      '200':\n        description: The booking details\n        headers:\n          RateLimit:\n            $ref: '#/components/headers/RateLimit'\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/Booking'\n          application/xml:\n            schema:\n              $ref: '#/components/schemas/Booking'\n\n\nNow let’s try that in OpenAPI 4.0 Project Moonwalk:\n\n  /bookings/{bookingId}:\n    parameterSchema:\n      bookingId:\n        description: The ID of the booking to retrieve.\n        type: string\n        format: uuid\n        examples: [1725ff48-ab45-4bb5-9d02-88745177dedb]\n    requests:\n      getBooking:\n        method: get\n        summary: Get a booking\n        description: Returns the details of a specific booking.\n        responses:\n          Ok:\n            status: '200'\n            contentType: application/json\n            contentSchema:\n              $ref: '#/components/schemas/Booking'\n\n\nThe nesting has been reduced by making status and contentType a new property in the response instead of another key and another level of nesting. I can remember keywords like status and contentType a lot easier than I can remember the order of that many keys and objects. Similarly the API requests definition have received the new method property avoiding yet another level of parent nesting.\n\nAnother nice benefit is the OAS “Parameters Object” being replaced with parameterSchema. This is a simple JSON Schema object, which is a lot easier for tooling developers to work with than stitch together object maps of “name” and “in”, where parameters are unique based on their location in the URL… This can be confusing for users too, when you get messages about “Parameter foo is not unique” because you had a foo defined in the Path’s Parameters Object, but a foo also defined in the query string in the Operations Parameters Object. This is just one place, with one list of parameters that can all be reasoned about together.\n\nA handy bonus is seeing Operation ID moved from in object to being a key. Operation ID is super handy for lots of tooling, from documentation to SDK generation, but it is rarely used. This means everyone will be defining it!\n\nSupport APIs that have different responses based on query parameters, headers and request bodies.\n\nFor starters, there is an explicit goal of allowing more types of API than only REST/REST-inspired APIs. This will now support all sorts of RPC API, which often uses fewer URLs, and sends a “method”, “action”, or “command” as a parameter in the request body or query string.\n\nThis would not have been possible in OpenAPI v3.x as a URL is expected to have one purpose only, and the shape of the response is expected to be the same regardless of what is sent.\n\nHere’s an example of an API that sends service.{serviceName} as a request header to decide what to do. This is a bit of a contrived example, but the goal is to show the flexibility of the new approach, where requests and responses can be defined to work in all sorts of ways, other than REST(ish) best practice. There’s nothing wrong with RPC, and the more APIs that can be described by OpenAPI the better.\n\nopenapi: 4.0.0\ninfo:\n  title: RPC API\n  version: 1.0.0\npaths:\n  \"/service\":\n    requests:\n      createFoo:\n        method: post\n        parameterSchema:\n          type: object\n          properties:\n            header:      ## We can either use this specially named property or create a first class headerSchema property on the Request Object\n              type: object\n              properties:\n                path:\n                  const: service.createFoo  ## path Header field used to convey the RPC method\n        contentType: application/json\n        contentSchema:\n          $ref: \"#/components/schemas/foo\"\n        responses:\n          ok:\n            status: 201\n            contentType: application/json\n            contentSchema:\n              $ref: \"#/components/schemas/foo\"\n      getFoo:\n        method: get\n        parameterSchema:\n          type: object\n          properties:\n            header:\n              type: object\n              properties:\n                path:\n                  const: service.getFoo ## path Header field used to convey the RPC method\n        responses:\n          ok:\n            status: 200\n            contentType: application/json\n            contentSchema:\n              $ref: \"#/components/schemas/foo\"\n      deleteFoo:\n        method: post\n        parameterSchema:\n          type: object\n          properties:\n            header:\n              type: object\n              properties:\n                path:\n                  const: service.deleteFoo\n        responses:\n          ok:\n            status: 200\n            contentType: application/json\n            contentSchema:\n              $ref: \"#/components/schemas/foo\"\n\n\nHere the parameterSchema is playing a role in deciding which method is being called, because the path property could be anything, but by defining const (constant value) it’s like writing a switch statement, where the case is the value of the const. Basically, if the path value matches this const value, a request will match this “operation signature” for this specific request. This can be used for data validation, documentation, SDK generation, and everything else, it’s just a bit of a different way to think about things. A more JSON Schema way.\n\nSupport a broader range of URL design patterns\n\nAs well as being able to work with more types of API than REST(ish), there are plenty of times REST API designers have had their hands tied by limitations on the sorts of URLs and parameter structures allowed in OpenAPI.\n\nFor example, in OAS3.x all path parameters have to be required, there is no way to have optional path parameters, or multi-segment path parameters.\n\nNeither of these sorts of URLs would be allowed in OAS3.1:\n\n\n  /files/{mypath}/{filename} - The mypath parameter would need to have slashes escaped and be considered one parameter.\n  /reports/{reportName}/{nonDefaultFormat} - The nonDefaultFormat would need to be provided every time, or two different operations would need to be defined with and without it, which can lead to a lot of duplication.\n\n\nAll of these things are common enough that the RFC 6570: URI Templates popped up back in 2012, and whilst OpenAPI paths look a bit like URI Templates they are only a subset of what it can do.\n\nProject Moonwalk once again says “hey there’s a standard for that, let’s use it!” and defers all path logic to the URI Template RFC. Using this RFC brings loads of powerful syntax, and adds support for loads of existing tooling which can make use of it.\n\npaths:\n  \"/files/{/filepath*}\":\n    # filepath can contain as many URI segments as needed\n\n  \"/reports/{reportName}{/nonDefaultFormat}\":\n    # this last one is now optional\n\n\nYou can do all sorts of advanced stuff, like define custom server names for particular operations should you have some need to do that, or work with all sorts of complex arrays or objects in the query string without having to decypher the arcane (and rarely supported) style/explode parameter combinations in OAS3.x.\n\n {/list}            /red,green,blue\n {/list*}           /red/green/blue\n {/list*,path:4}    /red/green/blue/%2Ffoo\n {/keys}            /semi,%3B,dot,.,comma,%2C\n {/keys*}           /semi=%3B/dot=./comma=%2C\n\n\nLearn more about URI Templates.\n\nWhen will OpenAPI v4.0 Be Released?\n\nIt’s still being worked on, but the OpenAPI Initiative have set the target release date for “sometime in 2024”.\n\nThere is always a delay between a specification being released and tooling vendors adding support for it, but unlike the v2.0 to v3.0 transition, the v3.0 to v3.1 was much quicker, and much easier. Whenever a specification like this ditches a unique snowflake of a concept and replaces it with an existing battle tested standard, the tooling migration gets a lot easier, as tooling vendors can wrap a few thousand lines of their code in an if statement, and the else is an existing library that has a whole bunch of tests.\n\nThere is already a lot of tooling out there helping with migrations, and the word on the grapevine from some of the modern tooling companies is that they’re already experimenting with OAS4 to make sure they’re ready when it drops.\n\nOngoing Discussions\n\nThere are still lots of ongoing discussions occurring, and things are likely to change somewhat before the final release, but now is a good time to be having a look around. Take a look at some of the examples, then a look at the ongoing discussions.\n\nReplacing $ref with “imports”\n\nThe $ref keyword is incredibly powerful, and can be used to “include” bits of OpenAPI and JSON Schema via a filepath, or a URL. This not only helps avoid repetition in a single OpenAPI document, but can be used to share components between multiple APIs, and can enable schema reuse across an entire organization (or even be published publicly and shared to others).\n\nSadly $ref has a history of not quite doing what people expect, not quite lining up with JSON Schema, and trying to keep up with changing expectations as those communities all continue to evolve. The reliance on the filesystem structure makes it hard to share documents with $ref, and lots of tools either don’t understand them, only support some of the functionality, or implement things incorrectly…\n\nThe latests versions of JSON Schema have standardized some really useful $ref resolutions that work outside of the filesystem, with an approach of “assuming you’ve got all these files from a repo, zip, floppy disk, whatever, they’ll all declare their $id and you can $ref on that. This has been great for the JSON Schema community, but the OpenAPI community has struggled to see any take-up on this at all.\n\nA new approach is being considered, conceptually referred to for now as “Moonwalk imports”.\n\nOpenAPI users can define a document like this, which for example purposes is purely using components.\n\nopenapi: 4.0.0\nself: https://example.com/fooComponents\ncomponents:\n  pathItems:\n    Foos: {...}\n  schemas:\n    Foos: {...}\n\n\nThen in another document they can define all the paths, and import than components document, referencing them with the namespace value to differentiate them from components defined in the local document.\n\nopenapi: 4.0.0\nself: https://example.com/fooPaths\ncomponents:\n  pathItems:\n    Bars: {...}\nimports:\n- namespace: foo\n  href: fooComponents\npaths:\n  /foos: foo:Foos\n  /bars: self:Bars\n\n\nThis is an interesting approach, which relies on RFC 3987: Internationalized Resource Identifiers (IRIs), keeping with the theme of replacing OpenAPI-specific things with existing standards.\n\nBetter Tags\n\nI am keeping a close eye on a discussion to improve Tags, because they’ve always felt a bit overloaded and confused with different tools having wildly different expectations for what they’re for and how they should be formatted.\n\nBetter tags · OAI sig-moonwalk · Discussion #67\n\nRemoving Discriminator\n\nThen there’s the discussion about removing discriminator entirely, because its been so poorly defined it’s been confusing people for years. It adds nothing on top of oneOf and does not exist in JSON Schema, so it’s hard to add support without custom writing an extension for specifically just that. Ditch it.\n\nReplace or remove discriminator · OAI sig-moonwalk · Discussion #57\n\nImproving Links\n\nLinks, can we upgrade the OAS 3 concept of Links from a rarely used or supported “Next Step hints” idea into something a bit more useful? Being able to link backwards as well as forwards. Being able to link to other servers using a full URL not just jump around the current API. It’s a lot more HATEOAS this way.\n\nProposal for links in OpenAPI v4\n\nIf you’re interested in Moonwalk, give it a shot. You can try out the concepts on your own OpenAPI documents to see how it looks. You’ll have to do that manually for now as tooling is waiting for the plan to settle, but Bump.sh will be keeping an eye on things and adding experimental support as soon as the time feels right. Until then keep getting feedback to the OAI so they can make this as good as possible."
        },
        {
          "id": "guides-bump-sh-tutorials-fastapi",
          "title": "Deploying docs from FastAPI",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Bump-sh tutorials",
          "tags": "",
          "url": "/guides/bump-sh-tutorials/fastapi/",
          "content": "FastAPI is a Python framework that allows you to create APIs, and automatically generates their documentation, based on the OpenAPI specification.\n\nThis guide walks you through how to deploy API documents generated by FastAPI, and how to enrich and filter those to improve the reader experience.\n\nDeploying docs from your local machine\n\nThe following assumes your local machine is configured with Python and FastAPI, and that your main file is named main.py.\n\n\n  \n    Create and name your first API documentation. Then, retrieve the name and token of this documentation from the CI deployment settings page.\n  \n  Install the Bump.sh CLI with npm as below, or use alternative options, with\n      npm install -g bump-cli\n    \n  \n  Launch your local server with\n      uvicorn main:app --reload\n    \n    Note: You might need, depending on how you usually run your Python commands, to prepend them with python3 -m.\n  \n  Deploy your doc to Bump.sh with\n      bump deploy http://127.0.0.1:8000/openapi.json \\\n --doc my-documentation-name \\\n --token my-documentation-token\n    \n  \n\n\nThat’s it! Enjoy the comfort of Bump.sh to browse through your API doc, and customize it to your needs.\n\nEnriching documents\n\nBy default, FastAPI generates what can be considered a plain technical description of your API. Although it is robust and complete, it’s not enough for people discovering your API without any upfront knowledge about it, access to the code, or ability to test it extensively. Such documents require contextual information, and any appropriate adjustements.\n\nFastAPI lets you extend documents, using Python.\n\nYou can also accomplish this using OpenAPI’s Overlay specification, which can be convenient in all use cases where you do not want to change your Python code base (e.g. when involving Technical Writers or Product Managers in writing docs).\n\nThe Bump.sh CLI supports Overlays. The bump overlay command will output a modified version of the [DEFINITION_FILE] (an OpenAPI or AsyncAPI document) by applying the changes described in the [OVERLAY_FILE] overlay file to the original API document. The bump deploy command can also take an --overlay parameter to skip the extra step.\n\n\n  \n    Create your Overlay document (see example use cases in Augmenting Generated OpenAPI Documents with Filters &amp; Overlays), and name it overlays.yaml (JSON is also supported).\n  \n  \n    Deploy the documentation (with overlays included) to Bump.sh.\n      bump deploy http://127.0.0.1:8000/openapi.json \\\n --doc my-documentation-name \\\n --token my-documentation-token\n --overlay overlays.yaml\n    \n  \n\n\n\n  Note: The diff computation performed by Bump.sh to display the API changelog is done when the resulting file is deployed.\nIf you need to compute the diff before applying Overlays (for instance when your Overlay document filters out some information), consider using the bump diff command and the GitHub Action."
        },
        {
          "id": "guides-bump-sh-tutorials-api-platform",
          "title": "Deploying docs from API Platform",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Bump-sh tutorials",
          "tags": "",
          "url": "/guides/bump-sh-tutorials/api-platform/",
          "content": "API Platform is an API-first PHP framework that allows you to create REST and GraphQL APIs using PHP classes, and automatically generates their documentation, based on the OpenAPI specification.\n\nThis guide walks you through how to deploy API documents generated by API Platform, and how to enrich and filter those to improve the reader experience.\n\nIf you are new to API Platform, check out their getting started guide.\n\nDeploying docs from your local machine\n\nThe following assumes your local machine is configured with PHP and API Platform.\n\n\n  \n    Create and name your first API documentation. Then, retrieve the name and token of this documentation from the CI deployment settings page.\n  \n  Install the Bump.sh CLI with npm as below, or use alternative options, with\n      npm install -g bump-cli\n    \n  \n  \n    Launch your local server with Docker or Symfony\n  \n  Deploy your doc to Bump.sh with\n      bump deploy https://localhost/docs.json \\\n --doc my-documentation-name \\\n --token my-documentation-token\n    \n  \n\n\nThat’s it! Enjoy the comfort of Bump.sh to browse through your API doc, and customize it to your needs.\n\nEnriching documents\n\nBy default, API Platform generates what can be considered a plain technical description of your API. Although it is robust and complete, it’s not enough for people discovering your API without any upfront knowledge about it, access to the code, or ability to test it extensively. Such documents require contextual information, and any appropriate adjustments.\n\nAPI Platform lets you override, enrich and filter the generated OpenAPI documents, using PHP.\n\nYou can also accomplish this using OpenAPI’s Overlay specification, which can be convenient in all use cases where you do not want to change your PHP code base (e.g. when involving Technical Writers or Product Managers in writing docs).\n\nThe Bump.sh CLI supports Overlays. The bump overlay command will output a modified version of the [DEFINITION_FILE] (an OpenAPI or AsyncAPI document) by applying the changes described in the [OVERLAY_FILE] overlay file to the original API document. The bump deploy command can also take an --overlay parameter to skip the extra step.\n\n\n  \n    Create your Overlay document (see example use cases in Augmenting Generated OpenAPI Documents with Filters &amp; Overlays), and name it overlays.yaml (JSON is also supported).\n  \n  \n    Deploy the documentation (with overlays included) to Bump.sh.\n      bump deploy https://localhost/docs.json \\\n --doc my-documentation-name \\\n --token my-documentation-token \\\n --overlay overlays.yaml\n    \n  \n\n\n\n  Note: The diff computation performed by Bump.sh to display the API changelog is done when the resulting file is deployed.\nIf you need to compute the diff before applying Overlays (for instance when your Overlay document filters out some information), consider using the bump diff command and the GitHub Action."
        },
        {
          "id": "guides-technical-writing-efficient-tech-writing-process",
          "title": "Efficient Technical Writing Processes for API Documentation",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Technical Writing",
          "tags": "",
          "url": "/guides/technical-writing/efficient-tech-writing-process/",
          "content": "The time of APIs being a hot new trend is long gone, and now the vast majority of tech companies are familiar with the process of building and maintaining APIs. The API First movement got teams putting more focus on their public APIs, “dog-fooding” them (using them internally to find kinks and quirks) and throwing huge effort into making documentation as simple as possible for onboarding and integration, but still “partner” or “integration” APIs are often being left behind.\n\nThese secondary APIs are no less important, in fact they may well be crucial to the business, but far too often it’s left as a spreadsheet or Google Doc that gets outdated as soon as it has been shared, and trying to improve the content or make sure all the endpoints are covered is a nightmarish job for tech writers.\n\nSo what’s the best approach to efficiently create thorough API documentation?\n\nWho Should Document APIs?\n\nThere’s no one role that should be entirely responsible for documenting an API. It will be developed by the engineering team, but they are not usually the best folks to write documentation. Not just because it takes a lot of skill and experience to put yourselves in the shoes of an end user who has no experience, but they could also be working on other features instead of trying to get them writing pages and pages of Markdown.\n\nWho else is there? An API may well be built according to requirements from API Product Manager, but that concept is so new there may not be anyone from product involved at all, other than somebody from business saying “can you integrate with company X somehow”?\n\nTechnical Writers are often expected to pretty much all of the work, but generating high quality documentation from thin air is incredibly difficult, especially if things are changing with the API rapidly.\n\nThere’s a middle ground to be found where everyone can leverage their very particular set of skills.\n\nGetting OpenAPI Involved Early\n\nThe sensible approach is to get tech writers involved earlier on, and using OpenAPI as a central source of truth, the engineers, product, and tech writers can all work around that format, making sure that the fundamentals are correct: endpoints, headers, request/response bodies, validation rules, etc. then the technical writers can plug in their excellent written word into the human-facing parts of the API description to help power amazing API reference documentation tools.\n\nHow exactly do you go about doing this?\n\nOption 1.) API Design First\n\nOnce you’ve finished whatever white-boarding and requirements gathering from your planning sessions, engineers and product people can start designing the API you’re thinking of building, whether that is using OpenAPI Editors, or just opening a text editor and writing OpenAPI by hand.\n\nAnother popular new option is to see how far you can get with things like ChatGPT or GitHub Copilot, who can turn a series of prompts like the following into a half-decent OpenAPI starting point.\n\n\n  Write an OpenAPI description for an API game of tic tac toe where multiple\ngames can be played at once, with endpoints for starting a new game, making a\nmove, and seeing the status of the game. Use OAuth 2 authentication and add\nerrors using the RFC 7807 format.\n\n\nHowever the editing is done, if the code and OpenAPI are living in the same Git repo, you can use the OpenAPI to power the code, then the technical writers can keep chipping in extra descriptions and improving the situation whilst the engineers keep adding new endpoints and properties over time.\n\nWe’ve written some guides on how to leverage the design-first workflow in Laravel PHP and Ruby on Rails, but there are great tools out there to help you with most popular languages/frameworks.\n\nOption 2.) Use a OpenAPI-aware framework\n\nTech writers don’t always have a say in the API design/development workflow, but if you do, and people aren’t interested in API Design First, try to recommend your engineers using OpenAPI-aware web application frameworks: it’ll make all your lives easier.\n\nUsing these modern OpenAPI-aware frameworks, the engineering team can start to build the API, and whenever the first prototype or beta is ready they can export the OpenAPI with a single command and it’ll be right there for you to pick up and extend.\n\n# Using Huma you can export OpenAPI from your code\ngo run . openapi &gt; openapi.yaml\n\n\nThis example from the Go framework Huma is just one modern OpenAPI-aware framework that can do this, but you can also consider API Platform for PHP, or FastAPI if Python is more your cup of tea.\n\nMost of these tools will export OpenAPI right over the top of the previous OpenAPI, overriding any previous changes. This is not ideal, and it’s likely these tools will eventually start offering “merge” functionality, but in the meantime how can you add valuable content to OpenAPI contributions without having it vanish on the next build?\n\nModifying OpenAPI Descriptions without losing your changes\n\nRegardless of how your OpenAPI is being changed, there are times when you might want to change some things without wanting to (or being able to) change the original. Whether that’s the code-first approach exporting OpenAPI, or some engineering teams using API Design First might produce it somewhere the technical writers cannot access, there will be times you need to modify OpenAPI to improve it for various audiences.\n\nA working group at the OpenAPI Initiative have released a new concept called “Overlays”. This is separate specification but compatible with OpenAPI, and while it’s still experimental it can be considered stable enough for people to start using.\n\n# overlays.yaml\noverlay: 1.0.0\ninfo:\n  title: Overlay to customise API for Protect Earth\n  version: 0.0.1\nactions:\n  - target: '$.info.description'\n    description: Provide a better introduction for our end users than this techno babble.\n    update: &gt;-\n      Protect Earth's Tree Tracker API will let you see what we've been planting and restoring all\n      around the UK, and help support our work by directly funding the trees we plant or the sites\n      we restore.\n      To get involved [contact us and ask for an access token](https://protect.earth/contact).\n\n\nUsing OpenAPI Overlays you can effectively “patch” an OpenAPI description, pointing to parts of the original document with JSONPath, then adding or updating your content in. You can add as many actions to these overlays as you like, or make multiple overlays.\n\nTo work with Overlays you’ll need a tool that understands them, and that’s not all OpenAPI tools as the concept is still very new. Regardless of what API documentation tool you are using, you can use the Bump CLI to apply these overlays, and this will produce a new user-facing document.\n\nbump overlay openapi.yaml overlays.yaml &gt; openapi.public.yaml\n\n\nYou can run these commands in continuous integration, and whatever you would have done with the original you can now do with the new openapi.public.yaml (or whatever you decide to name it).\n\nWhen deploying a document to Bump.sh, whether using the GitHub Action or the CLI, you can skip a step and point the deploy command at the overlay to have it handled automatically.\n\nname: Deploy documentation\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  deploy-doc:\n    name: Deploy API doc on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: partner-api\n          token: ${{secrets.BUMP_TOKEN}}\n          file: api/openapi.bundle.yaml\n          overlay: api/overlays.yaml\n\n\nLearn more about Overlays and using them within Bump.sh.\n\nAdding more context as Technical Writers\n\nWhether you’re using overlays or contributing to the original OpenAPI, what exactly should you be focusing on?\n\nEngineers will often focus very much on the “how”, but leave out some of the “why”, or really explain the “what”, so you can add this in.\n\nTags\n\nTags are a really useful place to explain some of the concepts being used. For example an Order and an Organization might seem fairly obvious what it is to the engineers working on it, but you could add context to them.\n\nHere’s an example of an overlay you could use to expand the tag. Descriptions with a whole bunch of Markdown, and links to other resources.\n\n# overlays.yaml\noverlay: 1.0.0\ninfo:\n  title: Overlay to customise API for Protect Earth\n  version: 0.0.1\nactions:\n  - target: '$.tags[?(@.name==\"Order\")]'\n    description: Provide more information for Order tag.\n    update:\n      description: &gt;\n        The Order resource represents a single order for trees, which can be fulfilled by one or more\n        deliveries. Orders are created by the [Protect Earth team](https://protect.earth/contact) and\n        are used to track the progress of your order from creation to delivery.\n  - target: '$.tags[?(@.name==\"Organization\")]'\n    description: Provide more information for Organization tag.\n    update:\n      description: &gt;\n        The Organization resource represents a single organization, which can be a charity, business,\n        or other entity. Organizations are created by the [Protect Earth team](https://protect.earth/contact)\n        and are connected to each of your Orders.\n\n\nThese descriptions (which can be much longer and full of even more Markdown) will then show up in API Documentation, pride of place, ready to explain the concepts to the user before they get stuck into what specific endpoints are about.\n\n\n\nHere’s the tag description rendered in Bump.sh.\n\nIntroductory Topics\n\nThere are quite a few handy “vendor extensions” around which you can add more power to any tooling that knows how to respond to them. One particularly useful one is x-topics, which allows tech writers (or anyone else messing with this sort of work known as “doc ops” or “spec ops”) to expand on just the API Reference Documentation, and start introducing end-users to other guides and content.\n\nopenapi: 3.1.0\n\nx-topics:\n  - title: Getting started\n    content:\n      $ref: ./docs/getting-started.md\n\n\nIn Bump.sh this will create a new navigation entry, and insert the Markdown content from the reference guide right into the main documentation.\n\n\n\nWhether you inject x-topics with Overlays or directly into OpenAPI in the source code, the result is the same.\n\nCode Samples\n\nThere’s countless other improvements you can make to the source OpenAPI given to you by the engineering teams who have other things to be worrying about, like adding client-side code samples with x-codeSamples.\n\npaths:\n  /users:\n    get:\n      summary: Retrieve a user\n      operationId: getUserPath\n      responses: [...]\n      parameters: [...]\n      x-codeSamples:\n        - lang: ruby\n          label: Ruby library\n          source: |\n            require \"http\"\n             \n            request = HTTP\n              .basic_auth(:user =&gt; \"name\", :pass =&gt; \"password\")\n              .headers(:accept =&gt; \"application/json\")\n             \n            response = request.get(\"https://api.example.com/v1/users\")\n            if response.status.success?\n              # Work with the response.body\n            else\n              # Handle error cases\n            end\n\n\n\nExternal Documentation\n\nYou could add externalDocs to point them to tutorials hosted elsewhere.\n\ntags:\n  - name: Stations\n    description: Train Stations all over Europe, using a bunch of standards defined elsewhere.\n    externalDocs:\n      url: https://train-travel.example.com/docs/stations\n\n\nFilter out anything that shouldn’t be there, like beta endpoints that are not ready for public use. There’s a few ways to do this.\n\nBump.sh users can do this with the x-beta property:\n\npaths:\n  /diffs:\n    post:\n      description: Create a diff between any two given API definitions\n      x-beta: true # Beta flag at the operation level\n      requestBody:\n        description: The diff creation request object\n        content:\n          application/json:\n            schema:\n              type: object\n              x-beta: true # Beta flag at the top-level schema object\n              properties:\n                url:\n                  type: string\n                  format: uri\n                  x-beta: true # Beta flag at the object property level\n                  description: |\n                    **Required** if `definition` is not present.\n                    Current definition URL. It should be accessible through HTTP by Bump.sh servers.\n\n\nOr you can filter them out with overlays:\n\noverlay: 1.0.0\ninfo:\n  title: Remove beta flags\n  version: 0.0.1\nactions:\n  - target: \"$..[?(@['x-beta'] == true)]^\"\n    description: Remove anything beta\n    remove: true\n\n\n\n  Learn more about working with JSONPath to write powerful targets for your overlays using our guide How to work with JSONPath.\n\n\nConsolidate multiple APIs and services to one Hub\n\nOnce you’ve created API Reference Documentation for a single API, why stop there? If you’ve got multiple APIs you can start to pull them all together into one place, so people don’t have to go rummaging around to find different bits of documentation.\n\nDifferent teams often end up implementing their own documentation in different ways, even when all using OpenAPI. Some bake it into the API and make it available on an endpoint like https://example.com/api/docs which is pretty handy for the developers, but that might be hidden behind various firewalls, VPNs, and authentication. Some teams export the docs as HTML and pop it into a /docs folder in the repo, but that locks the documentation away behind source control logins, and even with a GitHub pages setup thats going to look very different from your other docs.\n\nPulling all of your various APIs and disparate documentation hosting sources together into one place is usually known as creating an API Catalogue, and there are plenty of ways to do it. You can build it all yourself, or you can use an existing solution, like Hubs in Bump.sh.\n\nYou can pull in from multiple sources, and use bump deploy to push local files or even read URLs, and now you can run all the overlays you want in the process, to make it look brilliant and read excellently."
        },
        {
          "id": "guides-openapi-express-api-openapi",
          "title": "Creating an API with Express.js using OpenAPI",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "OpenAPI",
          "tags": "",
          "url": "/guides/openapi/express-api-openapi/",
          "content": "Express is a popular backend JavaScript framework for building landing pages and integrated content management systems or integrating APIs with other tools. With over twenty million weekly downloads on npm at the time of writing, the framework’s popularity comes from its ease of setup and use, extensibility with first- and third-party middleware functions, and its flexible built-in router.\n\nOpenAPI is a standard for describing HTTP APIs in a document that humans and computers alike can understand or consume. Building APIs according to the OpenAPI specification can ease friction between an API’s developer and its consumers, especially in terms of how the API should operate. Some knowledge about the OpenAPI specification can definitely help you understand the examples provided.\n\nIn this article, you’ll learn how to build REST APIs using Express. You’ll also learn how to document your APIs according to the OpenAPI specification with express-openapi. Finally, you’ll learn how to effectively manage your API documentation using Bump.sh.\n\nCrash Course in Express\n\nThe Express architecture is based around middleware, which are functions that can access and modify the request and response object and either return a response or trigger subsequent middleware functions. Middleware can be registered by invoking the .use() method on an Express application, like so:\n\nimport express from 'express';\nimport middlewareFunction from 'my-middleware-function';\n\nconst app = new express();\n\napp.use(middlewareFunction);\n\n\nExpress has three built-in middleware functions for serving static files (express.static, parsing JSON (express.json) and URL-encoded request payloads (express.urlencoded). Together with the Express router, these provide a good starting point for most applications.\n\nThe Case for OpenAPI\n\nThe OpenAPI specification is an opinionated, language-agnostic standard for describing HTTP APIs that allows humans and machines to understand and interact with an API without the need to access the source code. A valid OpenAPI description document is also called an API contract because, like a contract, it enforces a specific behavior that must be implemented by the developer and adhered to by the consumer.\n\nAn API contract adds value in many ways, including easing the development burden, improving ease of adoption for first-time consumers, and using automated tools to reduce the amount of work needed to generate client code and documentation or validate I/O data.\n\nIn the next few sections, we’ll see API contracts in action as we build an Express application and generate documentation with Bump.sh.\n\nImplementing an API with OpenAPI and Express\n\nIn this section, using Express, you’ll build an API that follows the OpenAPI specification. You will be walked through steps to set up an Express application, configure it according to the OpenAPI spec, and see how to view your API documentation.\n\nPrerequisites\n\nIn order to follow along with this tutorial, you’ll need the following:\n\n\n  Familiarity with JavaScript (and Node.js)\n  Node.js\n  A Node.js package manager — npm was used in this article\n  A Bump.sh account\n\n\nYou can find the source code for the project in this GitHub repo.\n\nCreating an Express Application\n\nLet’s start with creating an Express application. As mentioned, one of the reasons Express is so popular is that it’s quick and easy to set up.\n\nFirst, create a new folder for your project. Spin up a terminal session and run the following command to create a folder named express-oas:\n\nmkdir express-oas\n\n\nNext, initialize a JavaScript project by adding a package.json file. Using the same terminal session or via your computer’s file manager, create a file named package.json:\n\ncd express-oas\ntouch package.json\n\n\nOpen the package.json file using a text editor, then copy and paste the following in the file:\n\n{\n  \"name\": \"express-oas\",\n  \"main\": \"index.js\",\n  \"scripts\": {},\n  \"dependencies\": {\n    \"express\": \"^4.18.2\"\n  }\n}\n\n\nYou can also use npm init or yarn init to automatically generate a package.json file. Both npm init and yarn init (on Yarn Classic) run interactively, meaning that you’ll need to respond to a series of prompts before the file is generated. To skip these prompts and use the defaults, you can run the command with the -y flag:\n\n# Run interactively\nnpm init\n\n# Run non-interactively\nnpm init -y\n\n\nFinally, install the express package dependency from the npm or yarn registry and create your Express application.\n\nIn your terminal, run the command to install dependencies:\n\nnpm install\n\n\nNext, create a file named index.js in your project’s root folder. You can do that via the terminal by running the following:\n\ntouch index.js\n\n\nOpen this file using your text editor, then import and initialize your Express application:\n\nimport express from 'express';\n\nconst PORT = 3000;\n\nconst app = new express();\n\napp.use(express.json());\n\napp.listen(PORT, () =&gt; {\n\tconsole.log(`Server running on ${PORT}`);\n});\n\n\nThe default export from the express package is a function that, when invoked, creates an express application instance. This instance or object contains methods for routing HTTP requests, configuring middleware, and binding and listening for connections on a specified host and port.\n\nIn the code block above, you configured an Express application and registered the json() middleware, which parses incoming requests with JSON payloads and a matching Content-Type header.\n\nIf you try to access the application at this stage by running node index.js and navigating to http://localhost:3000 on a web browser, you’ll be greeted by an error. There’s no need to worry about this, however, as it just indicates that there are no routes or logic configured in your application yet. In the next section, we’ll add some logic and set up the application’s documentation with express-openapi, an OpenAPI framework for Express.\n\nIntegrating OpenAPI with express-openapi\n\nFor developing APIs, it helps to think of the API as a collection of resources, with each resource represented by a simple object that can be—from the moment of its creation—viewed, modified, or destroyed.\n\nFor simplicity, you can use this create, read, update, and delete (CRUD) pattern to help plan or design your API quickly. You can set up an OpenAPI-compliant API in a few steps using the express-openapi package. For this one we will assume that our project has only one resource, called User.\n\nexpress-openapi is an un-opinionated OpenAPI framework for Express, which supports OpenAPI versions 2.x and 3.0 at the time of writing. Configuration can be done in JavaScript or from a YAML string/file. In this project, you’ll be using a JavaScript object.\n\nexpress-openapi allows you to keep the OpenAPI documents in sync with the code. Basically, you will provide an OpenAPI document with empty paths and they will be populated from your code. Doing this ensures the OpenAPI document will be exactly reflecting how the code behaves, updated as the code is. It also allows you if you go play around with the tool to validate your schemas, automatically provide res.validateResponse tailored to a particular route, helps with your API security management, and so much more I can’t list them all now. The whole purpose of this framework is to stay as close as possible to express while leveraging the power of OpenAPI.\n\nTo get started, run this command in your terminal to install the package:\n\nnpm install express-openapi\n\n\nOpen the index.js file in your editor and add the following import near the top of the file:\n\nimport { initialize } from 'express-openapi';\n\n\nThe initialize import is a function that accepts a configuration object and sets up an OpenAPI-compliant contract that can be viewed or generated for your API. The required configuration parameters are as follows:\n\n\n  a reference to an Express application\n  an operations object containing exposed HTTP methods and handler functions\n  a paths string that points to a directory where route files can be found\n  an apiDoc object describing the API’s base definition, including schemas of objects used in your documentation\n\n\nNote that either operations or paths will be required at any time. This means that if operations is present, then paths isn’t required and vice versa.\n\nNow make the following changes to the index.js file to see what this config looks like in action.\n\nAs first argument, you need to pass a reference to your Express app, then optionally specify a path to the docsPath keyword if you want the API contract file to be served by your server, in development mode this can be useful (as you will see later when using the Bump CLI), however in production mode you might want to remove this and replace it with the exposeApiDocs: false option. Something like:\n\ninitialize({\n  app,\n  docsPath: \"/api-definition\",\n  exposeApiDocs: (ENV[\"NODE_ENV\"] !== \"production\"),\n});\n\n\nNext, configure the apiDoc object by specifying the path of a file containing the base of your API definition. In this tutorial, you’ll be using OpenAPI 3.1. Add the apiDoc file path to the initialize config object:\n\ninitialize({\n  // other objects here..\n  apiDoc: \"./doc/api-definition-base.yml\",\n})\n\n\nAnd create the file ./doc/api-definition-base.yml with the following content:\n\nopenapi: \"3.1.0\"\ninfo:\n  title: \"A getting started API\"\n  version: \"1.0.0\"\nservers:\n  - url: \"/\"\npaths: {}\ncomponents:\n  schemas:\n    User:\n      required: [\"name\"]\n      type: \"object\"\n      properties:\n        id:\n          type: \"string\"\n          description: \"The user's unique identifier\"\n        name:\n          type: \"string\"\n          description: \"The user's preferred name\"\n        email:\n          type: \"string\"\n          description: \"Email address\"\n\n\nNote that the API definition paths property is an empty object. This is because express-openapi will generate its members based on the value of the paths property given in the initialize function dynamically, which you’ll add next.\n\nTo do this, we will import javascript files by adding the folder path or our API endpoints logic to the initialize function call:\n\ninitialize({\n  // ...\n  // rest of code omitted for clarity\n  paths: \"./paths\",\n})\n\n\nFinally, you’ll need to add a route with some operations to complete the express-openapi initialization. The express-openapi package uses filesystem-based routing. Let’s look at the following routes we want to define:\n\nGET /users\nPUT /users/:id\n\n\nThey would need the following files to be created (assuming ./ is the starting directory):\n\n./paths/users.js\n./paths/users/{id}.js\n\n\nWhile we’ll be using more routes, these two files will be enough for what our project needs, so go ahead and create them:\n\nmkdir -p paths/users\ncd paths\ntouch users.js\ncd users\ntouch {id}.js\n\n\nWe’ll add some logic to our API application inside these files. Each file will contain a function as its default export, and this function will act as the corresponding path handler. For example, requests sent to /users will be handled by the function exported in ./paths/users.js, and requests sent to /users/{uniqueId} will be handled by the function exported in ./paths/users/{id}.js.\n\nFirst, open the users.js file and add the following code:\n\n\nexport default function () {\n  let operations = {\n    GET,\n    POST,\n  };\n\n  function GET(req, res, next) {}\n\n  function POST(req, res, next) {}\n\n  GET.apiDoc = {};\n  POST.apiDoc = {};\n\n  return operations;\n}\n\n\nNext, add the following code to the users/{id}.js file:\n\nexport default function () {\n  let operations = {\n    GET,\n    PUT,\n    DELETE,\n  };\n\n  function GET(req, res, next) {}\n\n  function PUT(req, res, next) {}\n\n  function DELETE(req, res, next) {}\n\n  GET.apiDoc = {};\n\n  PUT.apiDoc = {};\n\n  DELETE.apiDoc = {};\n\n  return operations;\n}\n\n\nLet’s quickly break down what’s happening in these files:\n\n\n  You have a function as the default export, and this function returns an object representing valid operations (HTTP methods) for the route.\n  As mentioned earlier, the file names correspond to the route that will be matched when your API is queried.\n  For each operation defined in the operations object, you need a corresponding handler function, and the function name must match the HTTP verb (PUT, GET, etc).\n  Finally, the documentation for each handler function can be described by adding an apiDoc property to the handler functions.\n\n\nWhat’s missing now are: your API documentation and the actual API logic. We’ll add those in the next few steps.\n\nFirst let’s add your API endpoints documentation. We will update the apiDoc property for the GET and POST handler functions in the users.js file:\n\n// ./paths/users.js\n// rest of code hidden for clarity\n\nGET.apiDoc = {\n    summary: \"Returns list of users\",\n    operationId: \"getUsers\",\n    responses: {\n      200: {\n        description: \"List of users\",\n        content: {\n          \"application/json\": {\n            schema: {\n              type: \"array\",\n              items: {\n                $ref: \"#/components/schemas/User\",\n              },\n            },\n          },\n        },\n      },\n    },\n  };\n  POST.apiDoc = {\n    summary: \"Creates a new user\",\n    operationId: \"createUser\",\n    requestBody: {\n      content: {\n        \"application/json\": {\n          schema: {\n            $ref: \"#/components/schemas/User\",\n          },\n        },\n      },\n    },\n    responses: {\n      201: {\n        description: \"Newly created user\",\n        content: {\n          \"application/json\": {\n            schema: {\n              $ref: \"#/components/schemas/User\",\n            },\n          },\n        },\n      },\n    },\n  };\n\n// rest of code hidden for clarity\n\n\nWe’ll also add the documentation for the GET, PUT, and DELETE handler functions in the users/{id}.js file:\n\n// ./paths/users/{id}.js\n// rest of code hidden for clarity\n\nGET.apiDoc = {\n    summary: \"Returns a single user\",\n    operationId: \"getOneUser\",\n    parameters: [\n      {\n        name: \"id\",\n        in: \"path\",\n        required: true,\n        schema: {\n          type: \"string\",\n        },\n      },\n    ],\n    responses: {\n      200: {\n        description: \"User data\",\n        content: {\n          \"application/json\": {\n            schema: {\n              $ref: \"#/components/schemas/User\",\n            },\n          },\n        },\n      },\n    },\n  };\n\n  PUT.apiDoc = {\n    summary: \"Updates an existing user\",\n    operationId: \"updateUser\",\n    parameters: [\n      {\n        name: \"id\",\n        in: \"path\",\n        schema: {\n          type: \"string\",\n        },\n        required: true,\n      },\n    ],\n    requestBody: {\n      content: {\n        \"application/json\": {\n          schema: {\n            $ref: \"#/components/schemas/User\",\n          },\n        },\n      },\n    },\n    responses: {\n      200: {\n        description: \"Updated user\",\n        content: {\n          \"application/json\": {\n            schema: {\n              $ref: \"#/components/schemas/User\",\n            },\n          },\n        },\n      },\n    },\n  };\n\n  DELETE.apiDoc = {\n    summary: \"Deletes an existing user\",\n    operationId: \"deleteUser\",\n    parameters: [\n      {\n        name: \"id\",\n        in: \"path\",\n        required: true,\n        schema: {\n          type: \"string\",\n        },\n      },\n    ],\n    responses: {\n      204: {\n        description: \"No content\",\n        content: {},\n      },\n    },\n  };\n\n\nThe apiDoc property of a function handler defines the OpenAPI documentation for that endpoint while the summary and operationId fields provide a brief description and a unique identifier, respectively. Route parameters and the required format for the request body data can be configured with the parameters and requestBody fields, respectively. Finally, the responses field provides information about the possible responses—and their status codes—for an endpoint.\n\nNext, update the function handlers to include CRUD logic:\n\n// ./paths/users.js\n\n// rest of code hidden for clarity\n\nfunction GET(req, res, next) {\n  res.status(200).json(mockDatabaseInstance.getAll());\n}\n\nfunction POST(req, res, next) {\n  const data = req.body;\n  mockDatabaseInstance.addUser(data);\n  res.status(201).json(mockDatabaseInstance.getAll());\n}\n\n\nThe GET and POST function handlers here respond to GET and POST requests made to /users, respectively. The GET function retrieves all user data from a database, the mockDatabaseInstance—which we’ll get to later—and returns it in JSON format with a status code of 200. The POST function adds a new user to the database from data provided via the request’s body and returns the updated user list with a status code of 201.\n\nWe’ll also need to update the function handlers in the /users/{id}.js file. Open the file and update the GET, PUT, and DELETE functions to include the following code:\n\n// ./paths/users/{id}.js\n// rest of code hidden for clarity\n\nfunction GET(req, res, next) {\n  const user = mockDatabaseInstance.getOne(req.params.id);\n  res.status(200).json(user);\n}\n\nfunction PUT(req, res, next) {\n  const data = req.body;\n  const updatedUser = mockDatabaseInstance.updateUser(req.params.id, data);\n  res.status(200).json(updatedUser);\n}\n\nfunction DELETE(req, res, next) {\n  mockDatabaseInstance.deleteUser(req.params.id);\n  res.status(204).send();\n}\n\n\nAs mentioned earlier, requests made to /users/someUniqueId will be routed to this file and handled according to their HTTP verbs. The GET function here retrieves a single user from the database, matching the ID provided in the request’s path. The PUT function updates a single user, also matching the provided ID, and returns the updated user as JSON with a status code of 200. The DELETE function removes a user from the database using the ID provided in the request. No data is returned from the DELETE function, so the status code is 204.\n\nThe mockDatabaseInstance object referenced in the function handlers code is a simple object with methods for updating and reading from an in-memory data store (an array of objects). We can add this to our project by creating a file named database.js in the project root:\n\ntouch database.js\n\n\nAnd adding the following code to the file:\n\nfunction mockDatabase() {\n  const dataStore = [];\n\n  function userExists(id) {\n    return dataStore.findIndex((value) =&gt; value.id === id) !== -1;\n  }\n\n  function addUser(data) {\n    if (userExists(data.id)) {\n      // user already exists, let's throw an error\n      throw new Error(\"User already exists.\");\n    }\n    dataStore.push(data);\n  }\n\n  function updateUser(id, data) {\n    if (!userExists(id)) {\n      // user does not exist, let's throw an error\n      throw new Error(`No user with ID ${id} was found`);\n    }\n    const index = dataStore.findIndex((value) =&gt; value.id === id);\n    const updatedUser = {\n      ...dataStore[index],\n      ...data,\n      id,\n    };\n    dataStore.splice(index, 1, updatedUser);\n    return updatedUser;\n  }\n\n  function deleteUser(id) {\n    if (!userExists(id)) {\n      // user does not exist, let's throw an error\n      throw new Error(`No user with ID ${id} was found`);\n    }\n    dataStore.splice(\n      dataStore.findIndex((value) =&gt; value.id === id),\n      1\n    );\n  }\n\n  function getAll() {\n    return dataStore;\n  }\n\n  function getOne(id) {\n    if (!userExists(id)) {\n      // user does not exist, let's throw an error\n      throw new Error(`No user with ID ${id} was found`);\n    }\n    return dataStore.find((value) =&gt; value.id === id);\n  }\n\n  return {\n    addUser,\n    updateUser,\n    deleteUser,\n    getAll,\n    getOne,\n  };\n}\n\nconst mockDatabaseInstance = mockDatabase();\n\nexport default mockDatabaseInstance;\n\n\nThe code above defines a function named mockDatabase that returns an object with multiple functions to perform CRUD operations on an in-memory data store, the dataStore array. The functions addUser, updateUser, and deleteUser respectively perform operations to add, update, or delete users from the data store. The getOne and getAll functions retrieve one or multiple users from the data store, while userExists is a utility function for checking if a user with a matching ID exists in the data store.\n\nWe instantiate and store the mockDatabase in the mockDatabaseInstance variable, and we export and use this in our function handlers. Let’s update the rest of our code to include the mockDatabaseInstance import.\n\nOpen the files in your paths directory, and add the following line at the top of each file:\n\nimport mockDatabaseInstance from \"../database.js\";\n\n\nNote that for the paths/users/{id}.js file, you need to add two dots and a slash (../) as it’s nested one level deep:\n\nimport mockDatabaseInstance from \"../../database.js\";\n\n\nWith that, your API and its contract have been set up. When you test your application in the next section, you’ll see that errors will be thrown for invalid requests or payloads that don’t conform to the documentation you’ve described. This is where the express-openapi library shines as you didn’t need to define any validation code but the provided documentation schemas and constraints will do all the job for you.\n\nWhile you’ve learned to create an Express application with express-openapi in this section, it’s been light on information about the OpenAPI specification and the express-openapi package. You can start with the OpenAPI guide if you’d like to learn more about the OpenAPI specification and the express-openapi documentation for more information on how to use the package.\n\nRunning and Testing the Application\n\nIt’s time to run and test the application. Simply run node index.js and navigate to http://localhost:3000/api-definition in a browser to see your API definition.\n\nYou can also test the API by visiting http://localhost:3000/users to list the users in your database.\n\nIn a production environment, you will probably want to deploy the updated API definition each time you make a change in your code without having to expose the API definition on your express server.\n\nTo do so, copy the following script into a new file named contract.js:\n\nimport OpenAPIFramework from \"openapi-framework\";\n\n// Use OpenAPIFramework similarly than in the express-openapi library\n// to generate the OpenAPI document of our API\nconst framework = new OpenAPIFramework.default({\n  featureType: 'middleware',\n  name: \"express-openapi\",\n  apiDoc: './doc/api-definition-base.yml',\n  paths: \"./paths\"\n});\nawait framework.initialize({});\n\n// Output OpenAPI documents\nconsole.log(JSON.stringify(framework.apiDoc))\n\n\nThis is a simple script which uses the express-openapi package to generate the API definition file without the need to run an Express server.\n\nSo if you run the following command:\n\nnode contract.js &gt; doc/api-definition.json\n\n\nYou will be able to snapshot the current OpenAPI documents file inside the doc/api-definition.json file.\n\nUsing Bump.sh for Documenting Your Express APIs\n\nUsing OpenAPI (and the express-openapi package) enables you to use API contracts that make collaboration easy among developers when building and integrating with APIs. However, manually generating and maintaining these contracts can be tough, as you just saw. Bump.sh can improve this collaboration by helping documenting your API based on the contract.\n\nAs an API documentation management solution, Bump.sh helps publish API contracts into developer portals, track changes, and alert teams when breaking changes are introduced. It essentially eases the workload of manually updating your API’s documentation, communicating changes made to your API’s consumers, and keeping track of all your product documentation.\n\nBump.sh provides a command line interface (CLI) tool that lets you easily preview your API documentation while it’s in development (with support for live reloading), deploy versions of your documentation automatically from your CI build step, compare changes made between versions of your API, as well as notify consumers of changes made to your APIs.\n\nConfigure Bump.sh for Express Applications\n\nTo configure Bump.sh for your Express app, you need to add the Bump.sh CLI to your existing Express project:\n\nnpm install bump-cli\n\n\nAs mentioned, bump-cli can be used to preview, compare versions, or deploy new versions of your API documentation.\n\nSign up or login in your Bump.sh  account. You can begin by navigating to your dashboard and clicking Create Documentation.\n\n\n\nNext, add your documentation’s name and, optionally, specify its access level (public or private).\n\n\n\nNext, you’ll be asked to upload a specification file. Choose the Use Bump.sh CLI option, which will immediately take you to the newly created documentation’s deployment configuration page. Here, you’ll find the token that’s required to deploy your documentation using the CLI. It’s the string labeled Access token, as shown below.\n\n\n\nCopy this token and replace YOUR_TOKEN with it when running the command to deploy your project:\n\nnpx bump deploy http://localhost:3000/api-definition --doc YOUR_DOCUMENTATION_SLUG --token YOUR_TOKEN\n\n\nRunning the command above will trigger a deployment that you can view by clicking View Documentation from your documentation’s General Settings page.\n\n\n\nYour deployed documentation should open in a new tab, and it should look like this:\n\n\n\nAutomate API documentation update with Bump.sh\n\nIn order to automatically deploy a new version of your documentation every time you push code changes to a branch, we will add the Bump Github Action to your repository.\n\nTo deploy a new version on each push, create a file named bump-deploy.yml in the .github/workflows folder of your project, then paste the following code into the file:\n\nname: Deploy documentation\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  deploy-doc:\n    name: Deploy API doc on Bump\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n      - name: Install NodeJS\n        uses: actions/setup-node@v3\n        with:\n          node-version: 18\n      - name: Install node dependencies\n        run: npm ci\n      - name: Generate OpenAPI contract\n        run: node contract.js &gt; doc/api-definition.json\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;BUMP_DOC_ID&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: doc/api-definition.json\n\n\nThis workflow runs every time git push is run on the main branch. It will generate the API definition thanks to the contract.js script, then deploy the API definition file to Bump.sh thanks to the Bump Github Action.\n\nJust make sure you provide your Bump documentation slug (replace the &lt;BUMP_DOC_ID&gt; value) and an encrypted secret containing the Bump access token used earlier in a BUMP_TOKEN secret variable on your repository.\n\nThe resulting action should run on each push to the main branch:\n\n\n\nYour API consumers can then subscribe to changes made to your API by clicking on API Changelog from your API documentation page. Either by completing the form or adding the RSS feed link to apps that can display RSS feeds.\n\n\n\nConclusion\n\nBy following this tutorial, you created an Express application and documented the API according to the OpenAPI specification with the express-openapi npm package. You also learnt how to deploy a live version of your API documentation using Bump.sh.\n\nDocumenting your APIs correctly is key to your API quality, usability, maintainability and helps your API consumers quickly get up to speed with using your product, reducing the number of support tickets and issues related to consuming your API.\n\nBump.sh helps with documenting your APIs but goes one step further by giving you tools like an automatic API changelog with notifications and diffs and a hub to manage all your API documentation in one place."
        },
        {
          "id": "guides-openapi-design-first-rails",
          "title": "Using OpenAPI to simplify building and testing Ruby on Rails APIs",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "OpenAPI",
          "tags": "",
          "url": "/guides/openapi/design-first-rails/",
          "content": "Ruby on Rails developers are blessed with a bunch of great OpenAPI tooling, and can use either of the API Code-First workflow which was popular for a long time, or the follow the newer API Design-first workflow.\n\nInstead of writing loads of code and sprinkling in some metadata later to create docs, the design-first workflow assumes you create the OpenAPI descriptions before writing any code at all. Once you have the OpenAPI description documents saved in your repository, you can leverage it at every step of the API lifecycle, to produce mock APIs for clients to test assumptions with, build client libraries without writing any code, make really effective contract testing, and even generate backend code to get the application teams started once the contract is all signed off.\n\nThis guide is going to look at two specific parts of the API design-first workflow that are most helpful to documentation, and show how to set it up in Rails: request validation automatically, and contract testing responses.\n\n\n  Getting OpenAPI \\&amp; Bump.sh Setup\n  Request Validation powered by OpenAPI\n  Contract Testing with OpenAPI\n  Sample Code\n\n\nGetting OpenAPI &amp; Bump.sh Setup\n\nThe API design-first workflow means you’ll need to create your OpenAPI description before you start writing all your code, so if you don’t have an openapi.yaml already that is probably the first step. You can use a wide variety of graphical editors, text editors, or traffic sniffing to generate this OpenAPI, and there is lots of documentation and guides to help you.\n\nAlternatively you can grab some sample OpenAPI from the API Guru marketplace, and download various OpenAPI descriptions in YAML or JSON format.\n\nEither way, once you have some an OpenAPI description document, pop it into your Git repository somewhere like api/openapi.yaml.\n\nBuilding an API for a bunch of clients is always a tricky one, but by deploying the documentation first you can see if people like the look of the API before you waste a bunch of time building it. Then as you progress through, especially if you are adding these tools to an existing codebase, you will continue to find mistakes in your OpenAPI or your actual API code, improving both as you go until you have a perfect match that will never again be broken, solving the “docs vs code” drift problem, and every fix will be deployed to Bump.sh with each commit/merge.\n\n$ bump deploy api/openapi.yaml \\\n  --doc rails-design-first \\\n  --token my-documentation-token\n\n* Your new documentation version will soon be ready at https://bump.sh/bump-examples/hub/code-samples/doc/rails-design-first\n\n\nInstead of using the CLI you could use GitHub Actions, or a bunch of other Continuous Integration.\n\nOnce Bump.sh is hooked up, let’s look at how we’d teach a Rails API (new, or existing) to be able to handle request validation for us.\n\nRequest Validation powered by OpenAPI\n\nInstead of wasting loads of time writing out validation logic in dry or whatever other DSL, why not just point it at an existing OpenAPI description document and skip repeating yourself? You don’t need to spend forever writing out that name is required, email is also required and an email address, date of birth is a date and optional… that’s what your OpenAPI description already says, and because it’s in a machine readable format you can just use it as code.\n\nStep 1: Add the openapi_first gems to your Gemfile.\n\n  # Gemfile\n  gem 'openapi_first', '~&gt; 1.0'\n\n\nStep 2: Run bundle install in the CLI.\n\nStep 3: Add the request validation middleware to the Rails application config.\n\n  # config/application.rb\n\n  require_relative \"boot\"\n\n  require \"rails/all\"\n\n  # Require the gems listed in Gemfile, including any gems\n  # you've limited to :test, :development, or :production.\n  Bundler.require(*Rails.groups)\n\n  module RailsDesignFirst\n    class Application &lt; Rails::Application\n      # ...snip... \n\n      # Add this line\n      config.middleware.use OpenapiFirst::Middlewares::RequestValidation, spec: 'api/openapi.yaml'\n    end\n  end\n\n\nStep 4: Start your server up and try it out!\n\n  rails s\n\n\nStep 5: Now using your favourite HTTP client you can try interacting with your API, to see how it works. Presuming you’ve got an endpoint, if not quickly make some sample controller (or grab ours from the sample code) and make sure the model has some required properties. A basic test is to try sending a request that misses out a required property, to see if that allows the request through or fails it.\n\n  curl -X POST http://localhost:3000/widgets -H \"Content-Type: application/json\" -d '{}'  | jq .\n\n  {\n    \"title\": \"Bad Request Body\",\n    \"status\": 400,\n    \"errors\": [\n      {\n        \"message\": \"object at root is missing required properties: name\",\n        \"pointer\": \"\",\n        \"code\": \"required\"\n      }\n    ]\n  }\n\n\nThis error is letting me know I missed the name property out of my request. By default these errors are in the format defined by RFC 9457: Problem Details for HTTP APIs, which is not just a good error format, but it means that various other tools you use throughout your stack can all be in the same format easily.\n\nAnyway, if we try with a valid request now the OpenAPI middleware should let the request through, and the API should respond with a success.\n\n$ curl -X POST http://localhost:3000/widgets -H \"Content-Type: application/json\" -d '{\"name\":\"Replicator\"}'  | jq .\n\n{\n  \"id\": 1,\n  \"name\": \"Replicator\",\n  \"created_at\": \"2024-01-08T16:27:14.151Z\",\n  \"updated_at\": \"2024-01-08T16:27:14.151Z\"\n}\n\n\nSuccess! Now, without needing to write any Ruby code at all, your API is rejecting invalid requests, which is not only saving time writing code, but is making sure the OpenAPI and code line up perfectly. It’s pretty hard for code and docs to drift when they’re sharing a single source of truth like this.\n\nSo long as you keep deploying OpenAPI changes to Bump using the CLI or GitHub Actions, now that your code is powered by your API it’s impossible to have any OpenAPI drift in your requests. Responses however, they still need to be checked, and we can do that with a regular test suite that you may well have already.\n\nContract Testing with OpenAPI\n\nIt can also power contract testing in your existing test suite, and openapi_contracts can help out.\n\nStep 1: Add the openapi_first gems to your Gemfile.\n\n  # Gemfile\n  gem 'openapi_contracts'\n\n\nStep 2: Run bundle install in the CLI.\n\nStep 3: Add this to spec/rails_helper.rb to let openapi_contracts know where your OpenAPI lives in the codebase. If this file does not exist make sure RSpec is setup and installed and run rails generate rspec:install.\n\n  # spec/rails_helper.rb\n  \n  RSpec.configure do |config|\n    \n    # add this line pointing to your openapi.yaml, mine is `api/openapi.yaml`.\n    config.before(:suite) do\n      OPENAPI_DOC = OpenapiContracts::Doc.parse(Rails.root.join('api'), 'openapi.yaml')\n    end\n  end\n\n\nStep 4: The way openapi_contract works is by adding a single assertion that can be used in request tests. Learn more about request tests with Rails and RSpec with this tutorial, but basically it looks a bit like this.\n\n# spec/requests/widgets_spec.rb\n\nrequire \"rails_helper\"\n\nRSpec.describe 'widgets', type: :request do\n  \n  describe \"GET /widgets\" do\n    it 'responds with 200 and matches the doc' do\n      get '/widgets'\n      expect(response).to have_http_status(:ok)\n      expect(response).to match_openapi_doc(OPENAPI_DOC)\n    end\n  end\n\nend\n\n\nAll the magic is happening in expect(response).to match_openapi_doc(OPENAPI_DOC), where it’s looking at the OpenAPI description, seeing which HTTP method and endpoint to look for, then comparing what it sees against the schema for the defined response.\n\nIf you get a response back in a test for a status code that is not defined in OpenAPI it will let you know:\n\nFailures:\n\n  1) widgets POST /widgets responds with 400 when invalid\n    Failure/Error: expect(response).to match_openapi_doc(OPENAPI_DOC)\n      * Undocumented response for \"POST /widgets\" with http status Bad Request (400)\n    # ./spec/requests/widgets_spec.rb:18:in `block (3 levels) in &lt;top (required)&gt;'\n\n\nVarious other problems were noticed, like my documentation saying POST /widgets would return with a 201 and an empty body, but the API was returning the entire object of the resource that was just created for no reason.\n\n  1) widgets POST /widgets responds with 201 when valid\n    Failure/Error: expect(response).to match_openapi_doc(OPENAPI_DOC)\n      * Expected empty response body\n    # ./spec/requests/widgets_spec.rb:17:in `block (3 levels) in &lt;top (required)&gt;'\n\n\n\nKeep experimenting with your OpenAPI and code responses until you’re happy with it all. See if you can break things, see if you can find uncovered endpoints, and keep making your code and OpenAPI better with every tweak.\n\nSample Code\n\nThe sample code for this design first guide is published on GitHub, so please\ntake a look at rails-design-first, and the deployed documentation is over here."
        },
        {
          "id": "guides-openapi-design-first-laravel-php",
          "title": "Using OpenAPI to simplify building and testing Laravel APIs",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "OpenAPI",
          "tags": "",
          "url": "/guides/openapi/design-first-laravel-php/",
          "content": "Laravel PHP is a powerful framework with loads of handy community extensions for building APIs, and working with OpenAPI tooling.\n\nSome folks may be used to the API code-first workflow, where you write the whole API then sprinkle in some metadata later using swagger-php or something similar. The API design-first workflow is the opposite of that approach.\n\nInstead of rushing into the code, we can build OpenAPI descriptions before writing any code at all, like creating a blueprint before building a house. Once you have the OpenAPI description documents saved (ideally in your source code repository), you can leverage it at every step of the API lifecycle, to produce mock APIs for clients to test assumptions with, build client libraries without writing any code, make really effective contract testing, and even generate backend code to get the application teams started once the contract is all signed off.\n\nThis guide is going to look at two specific parts of the API design-first workflow, and show how to set it up in Laravel: request validation, and contract testing responses.\n\n\n  Getting OpenAPI \\&amp; Bump.sh Setup\n  Request Validation powered by OpenAPI\n  Contract Testing with OpenAPI\n  Sample Code\n\n\nGetting OpenAPI &amp; Bump.sh Setup\n\nThe API design-first workflow means you’ll need to create your OpenAPI description before you start writing all your code, so if you don’t have an openapi.yaml already that is probably the first step. You can use a wide variety of graphical editors, text editors, or traffic sniffing to generate this OpenAPI, and there is lots of documentation and guides to help you.\n\nAlternatively you can grab some sample OpenAPI from the API Guru marketplace, and click JSON or YAML to download their OpenAPI descriptions.\n\nEither way, once you have an OpenAPI description document, pop it into your Git repository somewhere like api/openapi.yaml.\n\nBuilding an API for a bunch of clients is always a tricky one, but by deploying the documentation first, you can see if people like the look of the API before you waste time building it. Then, as you progress through, especially if you add these tools to an existing codebase, you will continue to find mistakes in your OpenAPI or your actual API code. Improve both as you go until you have a perfect match that will never again be broken, solving the “docs vs code” drift problem, and every fix will be deployed to Bump.sh with each commit/merge.\n\n$ bump deploy api/openapi.yaml \\\n  --doc laravel-design-first \\\n  --token my-documentation-token\n\n* Your new documentation version will soon be ready at https://bump.sh/hub/code-samples/doc/laravel-design-first\n\n\nInstead of using the CLI you could use GitHub Actions, or a bunch of other Continuous Integration.\n\nOnce Bump.sh is hooked up, let’s look at how we’d teach a Laravel API (new, or existing) to be able to handle request validation for us.\n\nRequest Validation powered by OpenAPI\n\nInstead of wasting loads of time writing out validation logic in PHP, why not just point it at an existing OpenAPI description and skip repeating yourself? You don’t need to spend forever writing out that name is required, email is also required and looks like an email address, date of birth is a date and optional… that’s what your OpenAPI description already says, and because it’s in a machine readable format you can just use it as code.\n\nWe’re working with some sample code in this guide, which has a basic POST /widgets endpoint. If I try to create a widget without providing the required description it will give me this database error.\n\n$ curl -X POST http://localhost:8000/api/widgets \\\n  -H \"Accept: application/json\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"Replicator\"}'\n\n{\n    \"message\": \"SQLSTATE[23000]: Integrity constraint violation: 19 NOT NULL constraint failed: widgets.description (Connection: sqlite, SQL: insert into \\\"widgets\\\" (\\\"name\\\", \\\"description\\\", \\\"updated_at\\\", \\\"created_at\\\") values (Replicator, ?, 2024-01-15 19:50:27, 2024-01-15 19:50:27))\",\n    \"exception\": \"Illuminate\\\\Database\\\\QueryException\",\n    \"file\": \"/Users/phil/src/laravel-design-first/vendor/laravel/framework/src/Illuminate/Database/Connection.php\",\n\n\nLet’s get this OpenAPI-based validation working so we don’t have to write a million validation rules that we’ve already written in my OpenAPI description.\n\nStep 1: Use Composer to install the membrane/laravel dependency.\n\ncomposer require membrane/laravel\n\n\nStep 2: Publish the membrane config so you can control how it works and help it find your OpenAPI.\n\nphp artisan vendor:publish --tag=\"membrane\"\n\n\nStep 3: Open config/membrane.php which was just created by that command, and update the location of our OpenAPI description documents.\n\n&lt;?php\n\ndeclare(strict_types=1);\n\nreturn [\n\n    'api_spec_file' =&gt; base_path() . '/api/openapi.yaml',\n\n\nThe “entry file” that is usually called openapi.yaml can live anywhere, but in this example it lives in the Laravel base path in a api/ directory.\n\nStep 4: Register the middleware in app/Http/Kernel.php, adding the following line to the appropriate middleware group.\n\n# app/Http/Kernel.php\n\n    protected $middlewareGroups = [\n        'api' =&gt; [\n            // ... \n            \\Membrane\\Laravel\\Middleware\\RequestValidation::class,\n            \\Membrane\\Laravel\\Middleware\\ResponseJsonFlat::class,\n        ],\n    ];\n\n\nStep 5: Start your server up so we can see if it all works.\n\nphp artisan serve\n\n\nStep 6: Now using your favourite HTTP client you can try interacting with your API, to see how it works. Presuming you’ve got an endpoint, if not quickly make some sample controller (or grab ours from the sample code) and make sure the model has some required properties. A basic test is to try sending a request that misses out a required property, to see if that allows the request through or fails it.\n\n$ curl -X POST http://localhost:8000/api/widgets \\\n  -H \"Accept: application/json\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"Replicator\"}'\n\n{\n  \"title\": \"Bad Request Body\",\n  \"status\": 400,\n  \"errors\": [\n    {\n      \"message\": \"object at root is missing required properties: name\",\n      \"pointer\": \"\",\n      \"code\": \"required\"\n    }\n  ]\n}\n\n\nThis error is letting me know I missed the description property out of my request. By default these errors are in the format defined by RFC 7807: Problem Details for HTTP APIs, which is not just a good error format, but it means that various other tools you use throughout your stack can all be in the same format easily.\n\nAnyway, if we try with a valid request now the OpenAPI middleware should let the request through, and the API should respond with a success.\n\n$ curl -X POST http://localhost:8000/api/widgets \\\n  -H \"Accept: application/json\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"Replicator\",\"description\": \"A device which can make anything out of recycled biowaste.\"}'\n\n{\n  \"data\": {\n    \"id\": 2,\n    \"name\": \"Replicator\",\n    \"description\": \"A device which can make anything out of recycled biowaste.\",\n    \"created_at\": \"2024-01-16T15:14:47.000000Z\",\n    \"updated_at\": \"2024-01-16T15:14:47.000000Z\"\n  },\n  \"links\": {\n    \"self\": \"http://localhost:8000/api/widgets/2\"\n  }\n}\n\n\nSuccess! Now, without needing to write any PHP code at all, your API is rejecting invalid requests, which is not only saving time writing code, but is making sure the OpenAPI and code line up perfectly. It’s pretty hard for code and docs to drift when they’re sharing a single source of truth like this.\n\nContract Testing with OpenAPI\n\nFor as long as you keep deploying OpenAPI changes to Bump.sh using the CLI or GitHub Actions, and as long as your code is powered by your API, it is impossible to have any OpenAPI drift in your requests. Responses however will still need to be checked, and we can do that within your existing PHPUnit or Pest test suite using a Laravel PHP extension called Spectator.\n\nThis guide will use the Pest test suite, but you can use PHPUnit if you prefer that.\n\nStep 1: Use Composer to install Spectator in your Laravel API.\n\ncomposer require hotmeteor/spectator --dev\n\nphp artisan vendor:publish --provider=\"Spectator\\SpectatorServiceProvider\"\n\n\nStep 2: Get a basic test running before we start trying to contract test.\n\nAssuming you’ve got Pest set up to work with Laravel, you probably have some tests that look a bit like this one, but if you’re missing tests you can make them with php artisan pest:test WidgetTest. Either way, a fairly common Laravel API endpoint test might look a bit like this:\n\n# tests/Feature/WidgetTest.php\n&lt;?php\nuse App\\Models\\Widget;\n\ndescribe('POST /widgets', function () {\n    it('returns a valid record', function () {\n        $this\n            -&gt;postJson(\"/api/widgets\", [\n                'name' =&gt; 'Test Widget',\n                'description' =&gt; 'This is a test widget',\n            ])\n            -&gt;assertStatus(201);\n    });\n\n    it('returns a 400 for invalid record', function () {\n        $this\n            -&gt;postJson(\"/api/widgets\", [\n                'name' =&gt; 'Missing a Description',\n            ])\n            -&gt;assertStatus(400);\n    });\n});\n\n\nAssuming you’ve got a test like this, or have made one along with the matching controller and model, then we’re all in the same place. Let’s get OpenAPI involved.\n\nStep 4: Add contract testing to your test suite.\n\nFirst let’s let Spectator know where we keep our API descriptions. We’re trying to use the api/openapi.yaml document so we can give it a base path that points it to that directory.\n\n# config/spectator.php\n&lt;?php\n\nreturn [\n\n    'default' =&gt; env('SPEC_SOURCE', 'local'),\n\n    'sources' =&gt; [\n        'local' =&gt; [\n            'source' =&gt; 'local',\n            'base_path' =&gt; './api/',\n        ],\n    ],\n\n\nThen open up tests/Pest.php and tell Spectator which OpenAPI description document it should be using:\n\n# tests/Pest.php\n&lt;?php\n\nuse Illuminate\\Foundation\\Testing\\RefreshDatabase;\nuse Spectator\\Spectator;\n\nuses(Tests\\TestCase::class)-&gt;in('Feature', 'Unit');\n\nuses(RefreshDatabase::class)-&gt;in('Feature');\n\nuses()-&gt;beforeEach(fn () =&gt; Spectator::using('openapi.yaml'))-&gt;in('Feature');\n\n\nOnce Spectator knows where your OpenAPI lives in the file system it can use it as the basis for contract testing assertions.\n\nStep 5: Update your assertions to be OpenAPI powered.\n\nThe way Spectator works is by adding assertionjs that can be used in Laravel Feature tests, which looks like this:\n\n# tests/Feature/WidgetTest.php\n&lt;?php\nuse App\\Models\\Widget;\n\ndescribe('POST /widgets', function () {\n    it('returns expected response when request is valid', function () {\n        $this\n            -&gt;postJson(\"/api/widgets\", [\n                'name' =&gt; 'Test Widget',\n                'description' =&gt; 'This is a test widget',\n            ])\n            -&gt;assertValidResponse(201);\n    });\n\n    it('returns a 400 for invalid request', function () {\n        $this\n            -&gt;postJson(\"/api/widgets\", [\n                'name' =&gt; 'Missing a Description',\n            ])\n            -&gt;assertValidResponse(400);\n    });\n});\n\ndescribe('GET /widgets/{id}', function () {\n    it('returns 200 for record that exists', function () {\n        $widget = Widget::factory()-&gt;create();\n        $this\n            -&gt;getJson(\"/api/widgets/{$widget-&gt;id}\")\n            -&gt;assertValidResponse(200);\n    });\n\n    it('returns a 404 for missing record', function () {\n        Widget::factory()-&gt;create();\n\n        $this\n            -&gt;getJson(\"/api/widgets/12345\")\n            -&gt;assertValidResponse(404);\n    });\n});\n\n\n\nAll the magic is happening in assertValidResponse(), where it’s looking at the OpenAPI description, seeing which HTTP method and endpoint being called, then comparing what it sees in the HTTP response coming from postJson against the OpenAPI descriptions response schema.\n\nImmediately Spectator got to work letting me know about mismatches between my code and the API description, and here are some highlights.\n\nFAILED  Tests\\Feature\\WidgetTest &gt; `GET /widgets/{id}` → it returns a 404 for missing record\nNo response object matching returned status code [404].\n\n\nOops, I am testing to see if a 404 appears but I have not actually described the 404, which means any API clients looking at the API documentation powered by this OpenAPI will have no idea that a 404 might appear. They might guess, but guesswork isn’t how you create a solid understanding of an API. Let’s define that.\n\nSpectator also let me know my errors are all the wrong shape. OpenAPI was suggsting { errors: { title, description, ... }} but the actual error format coming back from the API code was { title, description, errors: {}}.\n\nFAILED  Tests\\Feature\\WidgetTest &gt; `POST /widgets` → it returns a 400 for invalid request\n\nThe properties must match schema: errors\nThe data (object) must match the type: array\n\nobject++ &lt;== The properties must match schema: errors\n    errors: array &lt;== The data (object) must match the type: array\n        object++\n            title: string\n            detail: string\n            code: string\n\n\nIt shows how easy it can be to make mistakes and mismatches between your API and the OpenAPI description, and it shows how useful tools can be at pointing out the mismatches. Once you get this suite passing 100% you should never have other mismatches, and now you have extensive contract testing which will reduce general issues for your clients interacting with your API, avoid accidentally breaking changes, and you got all of that without having to spend infinite time writing out “and this property should be a string…” over and over again in PHP.\n\nSample Code\n\nThe sample code for this design first guide is published on GitHub, so please take a look at laravel-design-first, and the deployed documentation is over here."
        },
        {
          "id": "guides-bump-sh-tutorials-huma",
          "title": "Deploying docs from Huma",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Bump-sh tutorials",
          "tags": "",
          "url": "/guides/bump-sh-tutorials/huma/",
          "content": "Huma is a Golang framework that allows you to create APIs, and automatically generates their documentation, based on the OpenAPI specification.\n\nDeploying docs from your local machine\n\nThe following assumes your local machine is configured with Golang and you have a Huma app running on your local machine, and that your main file is named main.go. If you don’t have a Huma app set up, follow their Your First API guide.\n\nStep 1: Create and name your first API documentation. Then, retrieve the name and token of this documentation from the CI deployment settings page.\n\nStep 2: Install the Bump.sh CLI with npm as below, or use alternative options\n\nnpm install -g bump-cli\n\n\nStep 3: Launch your local server with\n\ngo run .\n\n\nThis will run not only the application, but it will make API documentation available on http://127.0.0.1:8000/.\n\nStep 4: Deploy your documentation to Bump.sh.\n\nbump deploy http://localhost:8888/openapi.yaml \\\n  --doc my-documentation-name \\\n  --token my-documentation-token\n\n\nExport OpenAPI for Continuous Integration\n\nRunning a web server and deploying that to Bump.sh might be ok locally, but if you’d like to automate everything properly it might become complicated to run an entire server instance on something like GitHub Actions. Let’s skip that, and configure Huma to output OpenAPI via a CLI Command.\n\nIt’s actually designed to do this, and whilst it’s not documented currently there is an example in the codebase:\n\nHere’s the important part:\n\n\tcli.Root().AddCommand(&amp;cobra.Command{\n\t\tUse:   \"openapi\",\n\t\tShort: \"Print the OpenAPI description\",\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tb, _ := yaml.Marshal(api.OpenAPI())\n\t\t\tfmt.Println(string(b))\n\t\t},\n\t})\n\n\nTo get this set up you can replace the standard main.go with something more like this:\n\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/danielgtaylor/huma/v2\"\n\t\"github.com/danielgtaylor/huma/v2/adapters/humachi\"\n\t\"github.com/go-chi/chi/v5\"\n\t\"github.com/goccy/go-yaml\"\n\t\"github.com/spf13/cobra\"\n)\n\n// Options for the CLI.\ntype Options struct {\n\tPort int `help:\"Port to listen on\" default:\"8888\"`\n}\n\n// GreetingInput represents the greeting operation request.\ntype GreetingInput struct {\n\tName string `path:\"name\" maxLength:\"30\" example:\"world\" doc:\"Name to greet\"`\n}\n\n// GreetingOutput represents the greeting operation response.\ntype GreetingOutput struct {\n\tBody struct {\n\t\tMessage string `json:\"message\" example:\"Hello, world!\" doc:\"Greeting message\"`\n\t}\n}\n\nfunc main() {\n\t// Store the API so we can access it from other commands later.\n\tvar api huma.API\n\n\t// Create a CLI app which takes a port option.\n\tcli := huma.NewCLI(func(hooks huma.Hooks, options *Options) {\n\t\t// Create a new router &amp; API\n\t\trouter := chi.NewMux()\n\t\tapi = humachi.New(router, huma.DefaultConfig(\"My API\", \"1.0.0\"))\n\n\t\t// Register GET /greeting/{name}\n\t\thuma.Register(api, huma.Operation{\n\t\t\tOperationID: \"get-greeting\",\n\t\t\tSummary:     \"Get a greeting\",\n\t\t\tMethod:      http.MethodGet,\n\t\t\tPath:        \"/greeting/{name}\",\n\t\t}, func(ctx context.Context, input *GreetingInput) (*GreetingOutput, error) {\n\t\t\tresp := &amp;GreetingOutput{}\n\t\t\tresp.Body.Message = fmt.Sprintf(\"Hello, %s!\", input.Name)\n\t\t\treturn resp, nil\n\t\t})\n\n\t\t// Tell the CLI how to start your router.\n\t\thooks.OnStart(func() {\n\t\t\thttp.ListenAndServe(fmt.Sprintf(\":%d\", options.Port), router)\n\t\t})\n\t})\n\n\t// Add a command to print the OpenAPI description\n\tcli.Root().AddCommand(&amp;cobra.Command{\n\t\tUse:   \"openapi\",\n\t\tShort: \"Print the OpenAPI description\",\n\t\tRun: func(cmd *cobra.Command, args []string) {\n\t\t\tb, _ := yaml.Marshal(api.OpenAPI())\n\t\t\tfmt.Println(string(b))\n\t\t},\n\t})\n\n\t// Run the CLI. When passed no commands, it starts the server.\n\tcli.Run()\n}\n\n\nOnce this is set up you can use these commands to work with the OpenAPI without needing to run a web server:\n\n# Output OpenAPI description to terminal\ngo run . openapi\n\n# Save OpenAPI description to a file\ngo run . openapi &gt; openapi.yaml\n\n\nDeploying to Bump.sh automatically through GitHub Actions now only involves running this command and pointing to the OpenAPI you created. This example will do both the deployment and the diff checking, but you can pick one or the other, or keep both.\n\nname: Check &amp; deploy API documentation\npermissions:\n  contents: read\n  pull-requests: write\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    branches:\n      - main\njobs:\n  deploy-doc:\n    if: ${{ github.event_name == 'push' }}\n    name: Deploy API documentation on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Set up Go\n        uses: actions/setup-go@v5\n\n      - name: Export OpenAPI to file\n        run: go run . openapi &gt; openapi.yaml\n\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: go-hello-openapi\n          token: ${{secrets.BUMP_TOKEN}}\n          file: openapi.yaml\n\n  api-diff:\n    if: ${{ github.event_name == 'pull_request' }}\n    name: Check API diff on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n      \n      - name: Set up Go\n        uses: actions/setup-go@v5\n      \n      - name: Export OpenAPI to file\n        run: go run . openapi &gt; openapi.yaml\n\n      - name: Comment pull request with API diff\n        uses: bump-sh/github-action@v1\n        with:\n          doc: go-hello-openapi\n          token: ${{secrets.BUMP_TOKEN}}\n          file: openapi.yaml\n          command: diff\n        env:\n          GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}\n\n\nThat’s it! Enjoy the comfort of Bump.sh to browse through your API doc, and customize it to your needs. Check out the Bump.sh GitHub Action or other continuous integration options.\n\nSample Code\n\nThe sample code for this guide is published on GitHub so you can try that if you’re having trouble adding it to your application: go-hello-openapi, and the deployed documentation is over here."
        },
        {
          "id": "guides-asyncapi-asyncapi-first-event-driven-api",
          "title": "An AsyncAPI Example: Building Your First Event-driven API",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "AsyncAPI",
          "tags": "",
          "url": "/guides/asyncapi/asyncapi-first-event-driven-api/",
          "content": "Event-driven APIs are APIs that use events to enable real-time and asynchronous communication between different components of a system. This leads to a couple significant benefits right out of the gate:\n\n\n  Clients receive real-time updates without constantly polling the server, which improves user experience.\n  System components can communicate without being directly connected to each other, so API producers and consumers can be more loosely coupled.\n\n\nWhy Use Event-driven APIs?\n\nLet’s break those two ideas down a bit so you can really see what’s going on.\n\nAn event is any change in state or an update that a client might be interested in, like a new user registration or an update in the payment status of a transaction. In an event-driven system, a component known as the publisher sends an event to a message broker. The message broker sends the event to all the other components, known as subscribers, that have registered to receive that particular event. This allows the components of the system to communicate in real-time without being directly connected to each other.\n\n\n\nAs an example, picture a chat application. When a user sends a message, an event-driven API can immediately notify all the other users in the chat, allowing the message to display in their chat window without needing to refresh the page. In a traditional synchronous API, the client would need to poll the server to check for new messages very often and then update the user.  With event-driven APIs, clients receive real-time updates from the API without constantly polling for changes, making them more efficient when it comes to network resource usage.\n\nIn a traditional synchronous request-response API, the components are tightly coupled. The consumer must make a request to the provider to receive data (say, for example, polling for new messages in a chat application). In an event-driven API, the components are decoupled. The publisher sends events to the message broker, which then broadcasts the events to the subscribers. The separate components can be more independent and flexible, since they don’t rely on direct communication with each other.\n\nHowever, in order for the system to work effectively, there must be a common understanding between the components regarding events and their data structures. This is where AsyncAPI comes in; it helps define a contract that describes how the components communicate and behave effectively.\n\nLet’s walk through the process of implementing an event-driven API using AsyncAPI, a specification for defining asynchronous APIs. We’ll also introduce Bump.sh, a tool for documenting and tracking event-driven APIs lifecycle/changes, and demonstrate how you can use it in conjunction with AsyncAPI files.\n\nImplementing a Node.js Event-Driven API with AsyncAPI\n\nAsyncAPI is a specification for defining the structure and behavior of event-driven APIs. It’s similar to OpenAPI, a specification for defining REST APIs.\n\nHowever, AsyncAPI has specific features for defining the events and subscriptions of an event-driven API. It provides a standardized way to describe the events, channels, and message formats used in an asynchronous API, making it easier for developers to understand and use that API.\n\nBefore we begin the process of implementing a Node.js event-driven API described using AsyncAPI, there are a few prerequisites that you will need to have installed:\n\n\n  Node.js\n  Git\n  Optional: Mosquitto, an open-source message broker that \t\t\t\timplements the MQTT protocol; this tutorial uses the public test server\n\n\nOnce you have these installed, you can follow the steps below to implement your event-driven API.\n\n1. Create an AsyncAPI File\n\nFirst, create an AsyncAPI file. This will define the events and their corresponding data that you’ll use in your event-driven system. It also defines the structure and behavior of your asynchronous APIs.\n\nAsyncAPI files are written in YAML or JSON and follow a specific format defined by the AsyncAPI specification. An AsyncAPI file consists of several main components:\n\n\n  asyncapi: Specifies the version of the AsyncAPI specification that the file follows; at time of writing, the latest available is v2.6.0, which is what this tutorial uses.\n  info: Contains metadata about the API, such as the title,  the version or the description of your API.\n  servers: Lists the servers that the API is available on, along with the protocol and any additional configuration required to use the server. In our testing API, that will be the network location of our message broker.\n  channels: Defines the channels that the API exposes, along with the operations that can be performed on each channel.\n  components: Defines reusable components that can be used throughout the AsyncAPI file, such as message payloads and security schemes. This section is optional, as you can define these components directly, but I do recommend it. As your AsyncAPI definition file starts to grow, this helps you avoid repeating yourself (e.g., when a message format is used in multiple channels).\n\n\nThe snippet below defines the first part of the AsyncAPI file for a chat application. It covers the general application descriptions:\n\nasyncapi: 2.6.0\ninfo:\n  title: Chat Application\n  version: 1.0.0\nservers:\n  testing:\n    url: test.mosquitto.org:1883\n    protocol: mqtt\n    description: Test broker\n\n\nThis AsyncAPI file defines a chat application using the AsyncAPI 2.6.0 version.\n\nIt also defines the servers block that specifies the network location of your message broker. The API application defined here uses the MQTT protocol and is available on the testing server at test.mosquitto.org:1883. This URL is a publicly available Mosquitto broker for test purposes; feel free to replace it with a local URL if you have Mosquitto installed on your environment.\n\nThe following section defines the channels available within the API. It defines the different operations the chat application can perform and their payloads.\n\nchannels:\n  chat:\n    publish:\n      operationId: onMessageReceieved\n      message:\n        name: text\n        payload:\n          type: string\n    subscribe:\n      operationId: sendMessage\n      message:\n        name: text\n        payload:\n          type: string\n\n\nThe channels section defines all the channels that the API exposes. In this case, there is a single channel called chat that exposes a publish and subscribe operation. The publish method allows clients to send messages to the chat application via the chat channel, while the subscribe method allows users to receive messages from the chat channel.\n\nThis might seem a bit counterintuitive as there are two sides to think about, the server application and the client. Instead, think about it this way: publish events are sent from the client to the application, while subscribe events are sent from the application to the clients.\n\n\n  Read more about pub-sub semantics here, then check out this proposal that aims to resolve the confusion between publish and subscribe events.\n\n\nIn the case of this tutorial, both operations have a very simple message payload of type string, which defines the text to be sent on the message. However, the message could be more complex and in this case described with JSON schema.\n\nYou can review and copy the full file here.\n\n2. Generate the Application Code with AsyncAPI Generator\n\nAfter creating your AsyncAPI file, you can use the AsyncAPI Generator to automatically generate the code for your event-driven application. The AsyncAPI Generator is a command-line tool that can generate code in multiple languages, including JavaScript, Python, and Go.\n\nTo use the AsyncAPI Generator, you will first need to install it using npm:\n\nnpm install -g @asyncapi/generator\n\n\nOnce you have installed the AsyncAPI Generator, you can use it to generate the code for your event-driven application.\n\n\n  The generated code is meant to be a starting point, not a comprehensive build-and-use solution.\n\n\nThe following command generates a functional node application from your AsyncAPI file. Make sure to replace the file and server names where appropriate.\n\nag asyncapi.yaml @asyncapi/nodejs-template -p server=testing  -o example\n\n\nThis command tells the AsyncAPI generator to generate Node.js code using the asyncapi.yaml file that you created in the previous step. The generator will generate the code for the testing server we have just defined with our AsyncAPI file, and output those new files to a folder named example. The AsyncAPI Generator supports code generation in many different languages such as Node, Java, Python, and Go.\n\nAfter running the command, you should have a full-fledged node application powering your event-driven application.\n\nYou can run the following to install the dependencies and run the application.\n\nnpm install\nnpm start\n\nYou should see the following output:\n\n&gt; node src/api/index.js\n\n SUB  Subscribed to chat\n PUB  Will eventually publish to chat\nChat Application 1.0.0 is ready!\n\n🔗  MQTT adapter is connected!\n\n\n3. Test the AsyncAPI Node.js Application\n\nTo test the application, you need to send messages to the broker as a publisher. You can do this using the MQTT.js library. Install it by running the following command in a separate terminal window:\n\nnpm install mqtt -g\n\n\nRun the following command to send a message to the message broker. This command sends a publish command to the test broker on the chat channel defined in your AsyncAPI file.\n\nmqtt pub -t 'chat' -h 'test.mosquitto.org' -m \"Hello World\"\n\n\nAfter running this command, check the original window running the chat application, and you should see the following output:\n\n← chat was received:\n'{text: Hello World}'\n\n\n\nThis output means your application successfully received your message from the message broker. If you want to start implementing business logic, you can review the code in the example directory.\n\nYou will find the handler for the messages under examples/src/api/handlers/chat.js. The file should look like this:\n\nconst handler = module.exports = {};\n\n/**\n * \n * @param {object} options\n * @param {object} options.message\n */\nhandler.onMessageReceieved = async ({message}) =&gt; {\n  // Implement your business logic here...\n};\n/**\n * \n * @param {object} options\n * @param {object} options.message\n */\nhandler.sendMessage = async ({message}) =&gt; {\n  // Implement your business logic here...\n};\n\n\n\nUsing Bump.sh with AsyncAPI\n\nBy creating your AsyncAPI file, you actually also created an API contract ! You can read all about them here but in short, an API contract specifies the components and describes the behavior of an API, for instance available events and data or the rules for using the API.\n\nYou saw this earlier with the chat application implementation; you had to define the different channels as well as the operations that a client could perform on each channel.\n\nHaving a clear and formal API contract can ensure that the different components of an event-driven system can communicate and work together effectively. It can also make it easier to collaborate with other developers and teams, since everyone will have a clear understanding of the API and how it’s working.\n\nYou can use Bump.sh to document and keep track of the changes in your event-driven API via its AsyncAPI API contract.\n\nTo use Bump.sh and generate your AsyncAPI based documentation, simply create an account. Then you can simply drag and drop your AsyncAPI file in the onboarding flow.\n\nAfter signing up, fill out the following form with the details of your API:\n\n\n\nYou can then proceed to upload your AsyncAPI file manually or using Bump.sh CLI.\n\nTo upload your file using the CLI start by installing it:\nnpm install -g bump-cli # using npm\nyarn global add bump-cli # using yarn\n\n\nThen preview your generated API documentation by running :\nbump preview path/to/file.json/yml\n\n\nThe CLI will respond with an URL that, if visited, will let you visualize for a few minutes how your API documentation would render in Bump.sh.\n\nWhen your AsyncAPI file is ready to be deployed, you can follow the simple steps of the Getting Started to have your API documentation live and ready to share.\n\nYou can also connect the Bump.sh CLI to CI/CD tools, and there is even a GitHub Action you can add to any project. Which will allow you to automatically track and inform about API changes and deploy new versions of your API, among other cool possibilities you can read about on the Bump.sh page in GitHub marketplace\n\nConclusion\n\nEvent-driven APIs lead to real-time, responsive, and loosely-coupled systems, and AsyncAPI is a great way to define those APIs. By using AsyncAPI and Bump.sh together, you can create high-quality documentation for all your event-driven APIs.\n\nFeel free to reach out to us if you have feedback, comments, or suggestions you’d like to share!"
        },
        {
          "id": "guides-api-basics-asyncapi-vs-openapi",
          "title": "AsyncAPI vs. OpenAPI: Which Specification Is Right for Your App?",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "API Basics",
          "tags": "",
          "url": "/guides/api-basics/asyncapi-vs-openapi/",
          "content": "Depending on how your application is designed and what it needs to accomplish, you probably want to consider choosing one type of communication protocol for your API over another—namely, synchronous or asynchronous. And which protocol you use determines which specification you need to follow for your API—OpenAPI or AsyncAPI.\n\nLet’s take a brief look at the differences between synchronous and asynchronous APIs, and then we’ll talk about whether OpenAPI or AsyncAPI is best for communicating the ins and outs of your API to your users.\n\nSynchronous vs. Asynchronous APIs\n\nSynchronous and asynchronous APIs differ mainly in how they process information.\n\nSynchronous APIs respond to each client request with only minimal elapsed time, and clients must wait for the response before code execution can continue. This is the type of API that supports most of the internet, and they typically use the REST protocol.\n\nAsynchronous APIs may provide a callback or notification when requested resources are ready, allowing clients to continue functioning without tying up resources waiting for a response from the API. These APIs use pub-sub messaging queues like MQTT and RabbitMQ, data streams like Kafka, or bidirectional communication protocols like WebSockets. IoT devices also commonly use asynchronous APIs.\n\nChoosing Between OpenAPI and AsyncAPI\n\nDocumentation of your APIs is critical for communicating between backend and frontend developers. And good documentation can go beyond just improving the development experience - it can even increase adoption of your API.\n\nIndustry-standard specifications like OpenAPI and AsyncAPI ensure that you’re effectively communicating to your users how they can interact with your API through an API contract. Which spec you use to define and document your API depends on your use case, available resources, engineering team and community support, and chosen protocol to transmit data.\n\n\n  In general, synchronous APIs can be defined using the OpenAPI standard, and asynchronous APIs can be defined using the AsyncAPI standard.\n\n\nProtocols\n\nOpenAPI is typically used for RESTful APIs, which leverage HTTP or HTTPS as a transport protocol for data. The client makes a request to the server, which responds quickly (ideally within milliseconds or seconds). HTTP is mostly used as a unidirectional protocol, meaning the connection is closed after a single request and response. Each request must create a new connection to the server.\n\nTo receive updates on any server changes, the client must rely on polling. Since the application does not know when a task will be completed, it must continue calling the API until the update is detected.\n\nAsyncAPI is based on event driven architectures (EDA). In this context, the sender and recipient don’t have to wait for a response from the other to do their next task or request.\n\nIn event driven architectures, as you might have guess, it all revolves around events. An event is defined as a change of state. \nIn an EDA, when an change of state occurs, the producer captures that event and sends it to all consumers who have subscribed to this event. There can be a broker in the middle in some cases. This way of architecturing APIs is refered to as Asynchronous.\n\nAsynchronous APIs can use bidirectional protocols, where a connection is maintained until it’s terminated by either the client or the server. Communication protocols like WebSockets allow the application to request data from the server but continue other processing while it waits for the response. Once resources are available to generate the response, the API produces a response, and the WebSocket connection streams the information back to the application.\n\nApplications can also subscribe to a channel on a message broker like MQTT, Kafka, or RabbitMQ where they can receive updates or notifications as events occur. When updates are no longer needed, the subscription can be terminated.\n\nA single API can make use of multiple protocols. We’ll talk more about that in a bit, but for now, suffice it to say that an API can combine synchronous and asynchronous methodologies, as well as use multiple asynchronous protocols. GraphQL APIs, for example, send queries over HTTP (a synchronous protocol), but also provide asynchronous messaging with WebSockets.\n\nSimplicity of Implementation\n\nSince OpenAPI and AsyncAPI support different communication protocols, they require different skills to implement and different amounts of work as well.\n\nTo implement an API designed according to OpenAPI, developers can use web application frameworks to set up the actual API. Authentication is also built into the standard, so everything needed to create your synchronous API is present once your OpenAPI document is created. Since synchronous APIs send messages directly to the endpoint, there’s no additional architecture required to build the API.\n\nOn the other hand, implementing an asynchronous API is more complicated. Developers can still use web application frameworks, but they do need more architecture support than that. If your API uses a message broker as a middleman between application and API, that needs to be designed and built. If you’re using WebSockets to push notifications, both the server and the application need listener functions that keep the connection open.\n\nCommunity Support\n\nAsyncAPI and OpenAPI are both open-source standards and part of the Linux Foundation. Both are stable, well-maintained specifications and should be supported well into the future. Still, there are a handful of differences when it comes to their ecosystems.\n\nOpenAPI is a more established standard, since it was developed for synchronous REST APIs. The OpenAPI specification has many contributors from the Linux Foundation and the community. Many more have contributed to the standard through discussion and examples. The governance model of the OpenAPI Initiative allows both community and industry contributions in an effort to keep the specification vendor-neutral. To contribute, you must meet requirements for membership in the initiative.\n\nAsyncAPI is a newer standard based on OpenAPI. While it has fewer code contributors than OpenAPI, its popularity is quickly growing. AsyncAPI attributes its rising success to its community support and all the hosted tools related to AsyncAPI. In late 2020, AsyncAPI also formed an open governance model to assure the community that no single company has control over AsyncAPI and that this initiative is, in fact, community driven. They even are a Linux Foundation Project since 2021.\n\nUse Cases\n\nNow, as we’ve already mentioned, whether you would use OpenAPI or AsyncAPI specification depends on what protocols your API uses. Which protocol you use depends on your specific use case and what backend resources are available for support. In general, OpenAPI is used to define APIs that provide immediate feedback (synchronous). The AsyncAPI spec defines APIs that don’t need to respond immediately to a request (asynchronous).\n\nBut let’s see how that works out in some everyday use cases. When does one implementation have an advantage over the other?\n\nRESTful APIs\n\nREST is a software architecture style commonly used to support APIs used on the web, and as mentioned earlier, synchronous APIs carry the day here. These are simple, uniform interfaces that are often used to support cloud applications and cloud services because they’re stateless, consistently available, and widely accessible. With these traits, a RESTful API can provide information on virtually any topic easily and reliably.\n\nInternet of Things\n\nInternet of Things (IoT) devices need to handle inconsistent communications between the device and the API. For example, an IoT device may go out of internet range periodically, but will still need to send data to the API for processing. This means that data may be sent in bursts to the API when a connection is available. IoT devices may also need to notify the API when a significant event has occurred, as when a smoke alarm goes off or a camera detects motion.\n\nAs a result of event processing, an IoT device may need to receive asynchronous notifications from the server. While the device itself holds raw data, servers collect and perform calculations of the data over time. The server may need to send notifications to the device when an event has occurred, like an IoT device losing power in your home.\n\nCombining Synchronous and Asynchronous APIs\n\nMany SaaS API offerings exist to set up shopping carts for users and store inventory management for owners. These solutions tend to include a mixture of synchronous and asynchronous APIs.\n\nInventory management applications, for example, use asynchronous API resources that allow users and owners to retrieve information about available inventory, user accounts, and pricing. Business owners can update their inventory, and it’s automatically available to users when the API pushes data to the message broker. However, remember that synchronous APIs support most of the internet. An online retail shop may very well want the reliability of a RESTful API as part of its system to facilitate communication with a wide array of other organizations and developers.\n\nDocumenting in Any Case\n\nRegardless of which specification you choose to develop your API with, users need to be aware of new features and changes. Maintaining your API’s documentation and ensuring that it keeps to the standards of your spec can be a time-hungry task.\n\nBump.sh transforms your OpenAPI and AsyncAPI documents into beautiful documentation. It lets your developers focus on building products rather than maintaining docs. Bump.sh provides automatic change detection by integrating with CI tools, pushing notifications of the change to stakeholders, and enabling validation via its simple-to-use interface. Take its free trial for a spin to see how Bump.sh can make documenting any API - OpenAPI or AsyncAPI - simple. You can also try any AsyncAPI or OpenAPI file in the preview at the bottom of this page.\n\nSo… Which one to pick?\n\nTo summarize, the API specification you should use depends entirely on what type of API you want to build. Long story short, synchronous APIs use simple, direct endpoint communication to link a client to a server. When data consists of simple requests that can be completed by the server quickly, a synchronous, RESTful API that follows the OpenAPI specification is what you want.\n\nAsynchronous APIs use message brokers or pub-sub to communicate between server and application as data is made available. They’re useful for real-time applications that require frequent updates. They can be more complex to implement since they require more architecture than synchronous APIs, but you can stay organized thanks to the AsyncAPI specification.\n\nBoth OpenAPI and AsyncAPI are open-source specifications and provide a standard method for designing and implementing APIs. A key asset of both specs is the ability to create comprehensive, industry-standard documentation for your API. By connecting your documents to Bump.sh, you can rest assured that your documentation is automatically updated for each API release."
        },
        {
          "id": "guides-api-basics-api-architecture-diagrams",
          "title": "Creating Better API Architecture Diagrams",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "API Basics",
          "tags": "",
          "url": "/guides/api-basics/api-architecture-diagrams/",
          "content": "Architecture diagrams are essential in the API development process. They provide a map of how different systems interact to help software teams manage and maintain them, and they provide insight into the architect’s vision for the entire system. Along with other elements like interactive API portals, API architecture diagrams also help enrich your API’s documentation.\n\nThis guide will share more about the various types of API architecture diagrams and some tips for creating them. You will see several examples of these diagrams and learn about the utility of UML for modeling them. Finally, you’ll get some best practices for creating architecture diagrams as a supplement to your API documentation.\n\nWhat are API Architecture Diagrams and Do You Need Them?\n\nAPI architecture diagrams are visual representations of the structure and interactions of the components of an API. They can help developers and architects plan the interface structure, users understand the intended use of the API, and they can also help QA engineers better test and debug the API.\n\nTypically, an API architecture diagram will identify the various API components, their relationships with each other, and the data flow between them, but as you’ll see in this piece, there are many types of API diagrams worth looking at.\n\nFor instance, this &quot;Kubernetes API structure&quot; diagram took around 10 hours of my time. pic.twitter.com/MeYGbaHlfV&mdash; Ivan Velichko (@iximiuz) February 1, 2021\n\n\nUnfortunately, creating a good API architecture diagram takes time. As the author of the Tweet above points out, this particular diagram took 10 hours of work to complete. So, creating diagrams like this can be helpful, but your engineering and documentation teams will need to make time to get them right.\n\nTypes of API Architecture Diagrams\n\nDepending on the API you’re documenting, the communication protocols you’re using, the complexity of the system, and the goals of the architecture diagram, you may diagram the API differently. In this post, I’ll primarily focus on diagramming REST APIs, but the types of API diagrams you can create are basically endless.\n\nThat said, here are a few interesting common types of API architecture diagrams:\n\nSequence Diagrams\n\nOne way to use an API diagram is to illustrate a specific workflow that consumers need to know about. A sequence diagram often includes the HTTP verb (GET, POST, PUT, or DELETE) and resource name for each API call in the diagram. They can also be useful for noting any custom calls (like bulk endpoints) that your API offers.\n\n\n\nThe API diagram above uses the order of arrows to indicate the order in which the calls should be made. Complex workflows like authentication and authorization often require API calls to be made in a very specific order, so API architecture diagrams can help clarify this.\n\n\nExample of a REST API sequence diagram with swimlanes.\n\nService Architecture Diagrams\n\nAnother way to use API architecture diagrams is to show how multiple services are connected and integrated with one another.\n\n\n\nThe diagram above shows how an online store’s architecture diagram might look, including the resources, endpoints, and attributes of each resource.\n\nService Architecture Diagrams help new users get a “lay of the land” when it comes to your API, meaning they can quickly see how all the resources and data models are related. This can help them plan out their application, especially if they’ll rely on several endpoints or services within your ecosystem.\n\nInternal API Architecture Diagrams\n\nFinally, your diagram might be focused on illustrating the internal architecture of your system rather than the external.\n\n\n\nThese diagrams are useful for other engineers at your company to see how the system is designed so they can better understand errors and debug errors faster. They help new engineers onboard faster, and can serve as a reference point when designing new systems at the company. Finally, they help with internal processes like security audits as knowing all the layers a request goes through before accessing your data can help identify vulnerabilities.\n\nTools for Creating API Architecture Diagrams\n\nNow that we have covered some types of API diagrams you might use, the next step is to figure out how to actually create the model for your API architecture diagram. There are many different tools available depending on your preference, team’s experience, and the purpose of your diagrams, but let’s take a look at three.\n\nUnified Modeling Language (UML)\n\nEngineers have a range of visual language models they can use to create graphical diagrams of software systems, but one of the most common standards for diagramming is Unified Modeling Language (UML). Many developers choose UML because of its:\n\n\n  Widespread use - Using a standard like UML ensures that multiple members of your team can pick it up and make modifications to the diagram in the future.\n  Flexibility - Although the UML has a vast library of symbols and concepts designed explicitly for creating software architecture diagrams, you can also customize its elements to fit whatever technologies your project uses or visual representations you want to create. UML is not limited to object-oriented software modeling, but is also commonly used to explain business processes.\n  Tooling - The popularity of the UML has led to the creation of a number of tools ranging from online tools for creating UML diagrams, to tools for generating code from diagrams. This ever-growing ecosystem of UML tools makes it more convenient than other languages of its kind.\n\n\nThe downside to UML is that you have to learn its way of doing things, so there’s a bit of a learning curve to it.\n\nMermaid\n\nUML is just one format for diagram design specifications. Another option is to use a code to diagram tool like Mermaid. Mermaid transforms plain-text (Markdown inspired text definitions in this case) into full-fleshed visual diagrams. Their tool is open-source and written in JavaScript, making it easy to customize if you’re so inclined. Advantages include:\n\n\n  Ease of Use - Markdown is a well-established language for developer documentation, and their live editor includes examples to help you get started.\n  Customizability - In addition to being open-source at its core, Mermaid allows you to build themes on top of the core product or use the API to generate diagrams programmatically.\n  Integrations - You can install Mermaid on your own server or embed it directly into your documentation or developer portal, making it one of the most flexible options out there.\n\n\nDownsides to Mermaid could be:\n\n  The default chart and diagram types are limited. As mentioned, you can customize it as much as you want to create new types of diagrams, but that takes a fair bit of work and programming knowledge.\n  Mermaid uses its own syntax. Even though it’s based on Markdown as mentioned above, it does take a bit of practice before you’ll be completely used to the tool.\n\n\nLucidchart\n\nAnother approach to creating API architecture diagrams is using a tool like Lucidchart, which sort of combines the best of a structured language like UML with an open-ended drawing tool. This makes Lucid good for its:\n\n\n  Adaptability - With Lucid, you can create your own shared library of diagram elements (or use a standard like UML). These diagram elements can become your team’s visual language and help API diagrams across your entire company look similar.\n  Collaboration - As a purpose-built team diagramming tool, Lucid allows you to version and share documents with your team or the public.\n\n\nThe downside to Lucid is that it’s a little more complex to learn than a freeform drawing tool and it’s not free. After your 7-day trial, you’ll be paying—as of today—at least $9 per month for an individual account.\n\nFinally, it’s worth noting that the three options above aren’t mutually exclusive, and there are many other tools you can use to create API architecture diagrams.\n\nBest Practices for API Architecture Diagrams\n\nOnce you know what kind of API architecture diagram you need and how you’ll go about creating it, there are still a few design best practices to keep in mind. Even if your diagram is only intended for internal use, making it visually appealing can help people digest the information better and avoid confusion.\n\nUse the Right Type of Diagram\n\nI’ve already discussed a few types of API architecture diagrams above, but it’s worth noting that you need to consider the goals of your diagram before picking the type of diagram you should create. Remember, a good diagram takes time to design and maintain, so don’t do work you don’t need.\n\nThink of Accessibility\n\nClarity is key when designing architecture diagrams. This is especially true for complex API diagrams, so it’s a good idea to use contrasting colors and shapes for each type of component. You can play with colors, shapes, and background patterns for each component so they’re easier to identify. Keep in mind that not all eyes work the same (for instance one in 12 men are colorblind), so high contrast between elements and text is very important. Tools like Sim Daltonism on osX can help ensure that your diagrams are readable for everyone.\n\nKeep it Simple and Avoid Redundancy\n\nMake sure your diagram is as straightforward to understand as possible. Avoid redundant elements or unnecessary complexity like intersecting lines. Also, promote readability by using swimlanes, leaving enough white space between diagram elements, and maintaining a uniform size of symbols and figures.\n\nAvoid Jargon\n\nTo promote clarity and readability, it’s good practice to use simple, easy-to-understand terms instead of technical jargon. In cases where this isn’t possible, at least explain your terms. You can do this by including a key or legend along with your diagram.\n\nGoing Further\n\nAs mentioned previously, API diagrams are a great way to help a user understand your API. They’re a great addition to the “Getting Started” section you’ll find in a lot of API documentation.\n\nBump.sh supports markdown descriptions and helps you share your API architecture diagrams via generated documentation based on an API contract.\nTo add an image to any markdown you just need the following syntax ![](https://rb.gy/i0zf0) where the link is a public URL to your image. More on that, brand new image sizing feature and best practices in the Images section of the documentation.\nIt you want to play even more with markdown, the x-topics property makes it easy to add relevant content sections to your documentation for a well-organized, reader-friendly structure.\n\nConclusion\n\nIn this article, you learned how API architecture diagrams can support your product and documentation efforts. You saw how diagrams can make systems that are easier to understand and maintain, and you learned about a few different types of API architecture diagrams that are available. Finally, you learned about a few tools and best practices for creating API architecture diagrams that are easier to read and maintain.\n\nDiagrams are just one tool in a developer’s toolbox for providing clarity around their APIs. You’ll also need documentation (which often includes relevant diagrams), an API contract, and a change management system.\n\nIf you’re looking for more ways to provide clarity around your API, check out Bump.sh. Import API contracts into Bump.sh and automatically generate human-readable documentation, with change-tracking and notification capabilities."
        },
        {
          "id": "guides-technical-writing-primitive-concepts-git",
          "title": "Primitive Concepts of Git",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "Technical Writing",
          "tags": "",
          "url": "/guides/technical-writing/primitive-concepts-git/",
          "content": "Git supports effective collaboration among large groups of people.\nUsing drawings and concrete examples,\nyou will learn how a few simple concepts can be used\nto support sophisticated social interactions.\nYou will learn enough of Git to decide whether you want Git in your toolbox,\nor perhaps why others in a collaborative project insist on using Git.\nAnd you will learn without touching a command line.\n\nGit was born to support Linux,\na large open source project developed by many people across the World.\nYou could hardly ask for a more inauspicious start:\n\n\n  Initial revision of “git”, the information manager from hell.\n— Linus Torvalds, 2005\n\n\nHowever, after satisfying the complex needs of Linux developers,\nGit adoption grew to the point when it is now the first choice for most people.\n\nFiles and trees\n\nFirst of all, Git stores files organized in a hierarchical tree,\njust like your typical file system does.\nIn particular, Git stores the contents of files and their pathnames.\nGoing beyond what a filesystem usually does, Git can store multiple trees.\n\nHere is an example of two trees with four files.\n\n\n\nCommits\n\nCommits give meaning to the multiple trees stored in Git.\nEach commit remembers that, at some date and time,\nsomeone (the author) asked Git to remember a specific tree,\nwith a message that typically states a reason.\n\nIn practice, people store multiple trees into Git because the trees are related to each other.\nFor that purpose,\nGit relates each commit with one or more previous commits,\ncalled the parents.\n\nFor example, you may want to remember the evolution of a tree over time,\nso that you can go back in time and recover previous revisions of files.\nEach commit would indicate the previous one as parent,\ncreating a sequence of points in time:\n\n\n\nMerge commits\n\nCommits get more interesting once you notice that\nGit can remember commits from multiple authors.\nCommit parents support collaboration among authors.\n\nPicking up the previous example,\nimagine that a colleague needed to work in the same tree of files.\n\n\n  \n    She picked up your work from last week, did her work,\nand committed her work today as “commit4”, as shown below.\n  \n  \n    During the same time, you also continued to work,\nand committed your changes yesterday and today.\n  \n\n\nFinally, you ask Git to combine your work with the work of your colleague.\nGit creates a merge commit with two parents\nthat combines the changes of both of you, as follows:\n\n\n  \n    If you and your colleague worked in different files,\nGit automatically takes the latest version of each file.\n  \n  \n    If you and your colleague worked in different parts of the same text file,\nGit automatically combines the changes into a single file.\n  \n  \n    Otherwise, you have conflicting changes.\nYou will have look at both revisions of the file\nand figure out what the final result should be,\nbefore Git can complete the merge.\n  \n\n\n\n\nNote how Git remembers that work diverged and later converged,\nincluding the before, the after, the between, who did what,\nand who merged the work.\n\nTags and branches\n\nIn the last section, we just told a little story of five commits,\nfrom commit1 to merge commit.\nA real project will accumulate many such stories,\nas several persons contribute their individual and collaborative work.\nFor example,\nthe Linux Kernel\n(the project that originated the development of Git)\nhas accumulated over one million commits\nsince 2005.\n\nTo navigate the avalanche of commits,\nGit has two ways to give names to commits:\n\n\n  \n    Tags name an event in time.\nFor example, the tag “v1.5” might name the tree\nthat was shipped to customers last week.\n  \n  \n    Branches name an effort, and point to the tip of a sequence of commits.\nBranches answer the question “where is the latest work on x?”\nBy convention, the branch “master” or “main” has the latest consolidated work.\n  \n\n\nBuilding on our example:\n\n\n  Tag “v1.5” remembers commit2 for posterity.\n  Branch “contrib” currently points to commit4, the work of your colleague.\n  Branch “main” currently points to the latest work, the merge commit.\n\n\n\n\nLast week, branch “main” pointed to commit1.\nAs you delivered work during the week,\nbranch main pointed to commit2, commit3 and, finally, the merge commit.\n\nWhen your colleague needed to start her work last week,\nshe did not have to ask you for the latest release:\nshe just picked up the files from branch main (commit1 at the time).\nToday, she delivered her work in the separate branch “contrib”\nand asked you to integrate her work into main.\n\nRebase and squash\n\nWhen a larger number of people collaborate over time on Git,\na large number of branches and merges\ncan make history difficult to understand.\nFor example,\na feature may have hesitated back and forth between alternatives and failures,\nwith the Git history showing every little change, every little mistake.\n\nGit has several ways to copy changes between branches,\nor to consolidate changes into a single commit.\nWhile the variations are beyond the scope of this article,\nwe will show how you might consolidate several related commits\ninto a single commit.\n\nFor our example,\nyou could use the Git operation “rebase” with the option “squash”\nto consolidate commit3, commit4, and the merge commit\ninto a new commit6, as shown below.\n\n\n\nThe result is a commit that has the same tree as the merge commit,\nbut that lost the memory of commit3, commit4, and the merge commit.\nThis simplified history may be useful or even required,\nbut it also has several consequences:\n\n\n  \n    Git remembers you as the sole author of commit6.\nThe contribution of your colleague is no longer distinguished\nfrom your own work,\nalthough the commit message may still mention her contribution.\n  \n  \n    Branch “main” now points to commit6.\nThe commit3 and the merge commit became orphans,\nin the sense that no branch and no tag knows about them.\nGit eventually removes these “unused” commits,\npossibly forgetting important work.\n  \n  \n    If your colleague continues to work on branch “contrib”,\nthe branch “main” no longer remembers\nthat she already delivered some of the work.\nYou may get surprising behavior when you try to merge her work again.\n  \n\n\nWork tree\n\nTo start working with Git, you initialize a Git repository within a directory.\nThe repository has a copy of the files you are working on,\nplus a special “.git” directory that stores\nyour multiple trees, files, commits, tags, and branches.\n\nThe example below has two files, “a.txt” and “dir/b.txt”,\ntogether with the magical “.git” directory.\n\n\n\nUsually you work on a branch as follows:\n\n\n  \n    You “check out” a branch\nto retrieve the files associated with the latest commit of the branch,\nreplacing the files in your directory.\n  \n  \n    You work on your files, perhaps changing, adding, and deleting files.\n  \n  \n    You commit some or all of your changes into Git,\nadding them to the current branch.\nGit tools show you a list of changes,\nand you decide whether to commit all changes, or only some of them.\n  \n\n\nClones and remote repositories\n\nIf Git is just managing directories in your computer,\nhow do you collaborate with others?\nGit is a distributed version control system,\nmeaning that each person has its own local repository,\nbut Git can synchronize changes\nbetween a local branch and a remote branch at another repository.\nYou can either pull commits from others, or push your commits to others.\n\nFor example, how can your colleague work on her computer\nand then deliver her changes to you?\nLast week, when your colleague needed to work on your files,\nyour repository had just the branch “main” with commit1.\nTo work on her own computer, your colleague did the following:\n\n\n  \n    She “cloned” your repository to a directory in her computer.\nGit copied your branches and commits,\nand automatically checked out the latest files\nfrom the “main” branch into her working tree.\n  \n  \n    She worked on the files on her working tree\nuntil she was ready to share her work with you.\nTo avoid any chance of conflict,\nshe committed her work to a new branch “contrib”.\n  \n  \n    She “pushed” her new branch “contrib” to your repository,\nfor you to get a copy of her work.\n  \n\n\nThis is what her repository looked like.\nNote that she has no idea that you delivered commit2 and commit3,\nalthough she could have pulled changes to the branch “main”\nto get those commits and synchronize her branch “main” with your work.\n\n\n\nTo coordinate the work of multiple people,\none of the repositories is designated the central one,\nand everyone clones, pulls, and pushes to that repository.\nThis central repository is typically hosted on a server and,\noften, on a platform such as GitHub or GitLab.\n\n\n\nPull requests\n\nYou probably did not have any problem\nwith your colleague pushing her work to your repository.\nBut she might as well have pushed changes to your “main” branch,\nand you might have sent those changes to a customer\nwithout reviewing her work.\n\nGit pull requests enable collaboration between people\nthat do not blindly trust each other.\nYour colleague would send you a request\nto merge her work into your branch “main”,\nand you would have the final word on accepting her changes.\nFor example, you might insist she addressed some concerns\nbefore you accepted her work.\n\nPlatforms such as GitHub and GitLab further support\nsophisticated collaboration among many people.\nBesides hosting central Git repositories,\nthese platforms support pull requests\nwith nice Web-based or native interfaces,\nand fine-grain control over who can pull, push, or merge.\nThey also add the ability for people to argue over a merge request,\nline-by-line if needed.\n\nFor example, suppose that your colleague\nneeded to work with another person working remotely,\nperhaps from a different organization.\nThey needed to collaborate over a longer period,\nexchanging work back and forth,\nuntil they finally submitted the work for your review\nin the form of a pull request.\n\nPlatforms such as GitHub or GitLab support that work\nby setting up a hosted clone of the central repository,\nwhich is commonly called a fork.\nYour colleague would collaborate with the other person over that fork,\npossibly using several branches,\npossibly using mutual pull requests.\n\n\n\nWhat have we learned so far?\nHere are the major concepts that you have learned in this article:\n\n\n  \n    Files in a tree:\n Store revisions of a hierarchy of files over time.\n  \n  \n    Commits:\n Remember when, why, and who asked Git to store each revision.\n  \n  \n    Parents:\n Track history automatically,\n either when work diverges (new branch)\n or when work converges (merge).\n  \n  \n    Tags:\n Name a specific revision, typically a delivery or a release.\n  \n  \n    Branches:\n Track work streams.\n Remote branches synchronize work across repositories.\n  \n  \n    Working directory:\n The directory where you work,\n while Git remembers your previous work in the “.git” directory\n (the repository).\n  \n  \n    Pull request:\n Polite request to deliver work to another repository.\n Platforms such as GitHub and GitLab\n enforce policies and support conversation over changes in pull requests.\n  \n\n\nWhat next?\n\nThese concepts should be enough for you to figure out\nwhether Git is a good solution for you.\nTo actually use Git,\nyou will need to adopt a tool and learn its peculiarities.\nAnd that’s when the journey gets interesting.\n\nAt its heart, Git is a simple solution that supports complex behaviors.\nAlthough all graphical Git tools support the basic functionality,\nthey differ in advanced functionality,\nand they can favor different ways of working.\nFor example, some graphical tools emphasize the current branch,\nwhile other graphical tools show all branches.\nThat’s why most Git guides adopt the command line as a neutral approach.\n\nWhile the command line is available in every system and can be extremely effective,\na graphical Git tool helps visualize the state of files in the working directory, \nvisualize the history of commits in the repository,\nand understand what changes you are about to commit.\nI can vouch for two open source tools:\n\n\n  \n    TortoiseGit,\na Windows shell extension that excels at showing changes in the working directory,\nand that supports advanced needs as well.\nI have used TortoiseGit professionally,\nand I have successfully taught non-technical people to use this tool.\n  \n  \n    GitX,\na MacOS application devoid of advanced functionality,\nbut enough for daily use.\nI use GitX for my personal repositories.\n  \n\n\nWhatever the tool you choose, including the command line,\ntwo habits will help you collaborate with others over Git:\n\n\n  \n    Always review your changes before you commit.\nThis practice will stop you from confusing your colleagues\nby committing spurious files or\nby mixing unrelated work in the same commit.\n  \n  \n    If anything feels off with your Git repository,\nstop and ask for help before you get in trouble.\nExperts have learned to respect Git,\nin the same way you would respect a sharp tool.\nExperts know the corner cases that can trap the unwary.\nI learned both from colleagues and from the Internet,\nbefore I also helped colleagues stay out of trouble.\nBeware of rebases and merge conflicts.\n  \n\n\nGit is one of those tools that takes minutes to learn and a lifetime to master.\n\n\n  \n    The Git-scm home is a good place\nto download a Git client and explore the official documentation.\n  \n  \n    The freely available book\nPro Git\nby Scott Chacon and Ben Straub\nwas instrumental in my journey to relative Git mastery,\nand was also an irreplaceable reference for this article.\n  \n\n\nTo Git or not to Git, that is your question now.\nI wish you a nice journey."
        },
        {
          "id": "guides-openapi-augmenting-generated-openapi",
          "title": "Augmenting Generated OpenAPI Documents with Filters & Overlays",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "OpenAPI",
          "tags": "",
          "url": "/guides/openapi/augmenting-generated-openapi/",
          "content": "However you make OpenAPI descriptions for your APIs, there are all sorts of scenarios where you might want to customize it for different audiences. Perhaps your tech writers want to add amazing longer descriptions but they are locked out of the source code, or you want to hide some internal endpoints from your OpenAPI before publishing. Is it possible to do all this without  awkwardly managing multiple similar-but-different OpenAPI documents?\n\nThis article will show you how you can slice and dice your OpenAPI descriptions in a bunch of handy ways to support complex workflows.\n\nHide some endpoints you don’t want to show\n\nHow often have you had a private API that’s suddenly become a partner API (used by other companies you work with) or a public API (used by literally anyone that can get their hands on it) without you really planning it?\n\nGood on you if you said “never”, but right now I have an API exposing endpoints it shouldn’t in the docs. The Protect Earth API is showing the “Upload Trees” endpoint which is exclusively used by our iOS application to upload photos of trees we planted, and nobody can do anything with it unless they have a special access token, so why even have it there?\n\nLet’s fix that right now and have Bump.sh CLI upload the result.\n\nFirst of all we’re going to mark that upload with an x-internal extension flag. This is a common convention in OpenAPI-land, and will not cause any problems for tooling that does not support it.\n\n  '/upload':\n    post:\n      summary: Upload a Unit\n      description: 'Internal endpoint for iOS app only, to upload a unit from the field.'\n      operationId: post-upload\n      x-internal: true # 👈 new \n      security:\n        - \"ApiKey\": [ ]\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/Upload'\n\n\nWith that flag popped in we now have something to filter on using the sensibly named openapi-filter.\n\n$ npm install --global openapi-filter\n\n$ openapi-filter --flags x-internal -- api/openapi.yaml api/openapi.public.yaml\n\n\nRunning the NPM script will filter out anything that has the flags specified, so this should removed the whole Upload operation from the source document called openapi.yaml, and shove it in a new document called openapi.public.yaml. Now I can deploy from this filtered version and the Uploads endpoint will be gone.\n\n$ bump deploy api/openapi.public.yaml -d &lt;DOC-ID&gt; -t &lt;TOKEN&gt;\n* Your new documentation version will soon be ready at https://bump.sh/green-turtle/doc/tree-tracker-api\n\nLet's deploy a new version to your &lt;DOC-ID&gt; documentation on Bump.sh... done\n\n\nTake a look in there and there’s no more Upload! Phew. Right, with that sorted how can I get this working on every push? I’ve already set up a .github/workflows/bump.sh which runs on any pushes to main, so I can modify that a bit to use this new command:\n\nname: Deploy API documentation\n\n# snipped standard Bump.sh deploy GitHub Action...\n\njobs:\n  deploy-doc:\n    if: ${{ github.event_name == 'push' }}\n    name: Deploy API documentation on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      # added this\n      - name: Setup node\n        uses: actions/setup-node@v3\n        with:\n          node-version: lts\n          cache: npm\n\n      # and this \n      - name: Filter internal endpoints\n        run: |\n          npx openapi-filter -- --flags x-internal api/openapi.yaml api/openapi.public.yaml\n\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;DOC-ID&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: api/openapi.public.yaml # 👈 updated this\n\n\nNow whenever anyone commits it’ll publish the automatically created filtered openapi.public.yaml instead of the original version, and we won’t even have to think about it again.\n\nYou could also use this approach to deploy two different sets of documentation, maybe on different Bump.sh Hubs for different teams with different permissions. 🙌\n\nAnother scenario: what if we have been adding some new endpoints which aren’t quite ready yet? Bump.sh users can use the x-beta: true vendor extension and filter that just as you would with x-internal. Another approach would be to use tags to mark things as beta, because openapi-filter will let you use tags as flags with the --checkTags flag. Here we can look for the Beta tag, and filter out any operations that use it.\n\n  '/webhook/orders':\n    post:\n      operationId: post-orders\n      summary: Save Shopify Order\n      tags:\n        - Webhooks\n        - Beta\n      requestBody:\n        content:\n          # snip\n\n\n$ openapi-filter -- --flags Beta --checkTags api/openapi.yaml api/openapi.public.yaml\n\n\nThat extra --checkTags means openapi-filter will look into the tags array, and remove any that have tags mentioned in flags!\n\nFor the Bump.sh GitHub Action that would look like this:\n\n      - name: Filter beta endpoints\n        run: |\n          npx openapi-filter -- --flags Beta --checkTags api/openapi.yaml api/openapi.public.yaml\n\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;DOC-ID&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: api/openapi.public.yaml # 👈 updated this\n\n\nOk, enough with removing things. What about adding and changing things?\n\nEnriching OpenAPI with Descriptions &amp; Examples\n\nProduct, marketing, or tech writers often focus more on the  “why” and “how” of the OpenAPI descriptions, helping prepare them to be used as API Documentation after developers have traditionally set up the skeleton based mainly on the “what”.\n\nFor teams following an API Design First workflow (where you’re managing your description documents as a source of truth and using them to power/test your code) then you probably don’t have a problem, as your non-technical users can just modify those files, but teams who are following API Code-first have some problems to deal with here. When generating OpenAPI from source code with comments or annotations, or generated from HTTP traffic, any changes made to the generated OpenAPI will be lost next time they generate. How can that be avoided?\n\nOpenAPI has a new experimental extension called Overlays, put together by the OAI (OpenAPI Initiative), tooling vendors, and community members that all came together to make a specification.\n\nIt’s early days for the standard, but Bump.sh has added experimental support for Overlays in the Bump CLI, meaning you can use it right now.\n\nnpm install -g bump-cli\n\n\nLet’s take a look at some example use cases.\n\nExample: Update API Description with Overlays\n\nHere we have a fairly generic openapi.yaml which was published by another team, not really intending it for a public audience.\n\nopenapi: 3.1.0\ninfo:\n  title: Tree Tracker API\n  description: 'Stock management for tree planting, and biodiversity improvement.'\n\n\nNot very exciting is it! If this were your API, the technical writers would want to explain things a bit better, and the developer experience people want to outline where you could go to get access tokens, so lets make an overlay that does that.\n\nThe way Overlays work is to look up bits of the OpenAPI description using something like a CSS selector or XPath if you’re familiar with that. Spectral users will recognize this for sure. Once you’ve pointed the overlay to a bit of the OpenAPI description, you can apply an action, which is either update or remove.\n\n# overlays.yaml\noverlay: 1.0.0\ninfo:\n  title: Improve the API's main description\n  version: 0.0.1\nactions:\n  - target: '$.info'\n    description: Provide a better introduction for our end users than this techno babble.\n    update:\n      description: &gt;\n        Protect Earth's Tree Tracker API will let you see what we've been planting and restoring all\n        around the UK, and help support our work by directly funding the trees we plant or the sites\n        we restore.\n        To get involved [contact us and ask for an access token](https://protect.earth/contact) then\n        [check out the API documentation](https://protect.earth/api).\n\n\nLet’s break this down a bit.\n\nThe target is $ (root), then .info goes into the info: object at the root of our OpenAPI document. Now that we’ve pointed to the right bit of the OpenAPI document, we can update it, which in this example will update the object to contain a new description property, with a much longer description that’s got handy Markdown (CommonMark) in there.\n\nNow we’ve got the original openapi.yaml and this new overlays.yaml document sitting around, how can we actually get these overlays applied? With the Bump.sh CLI! Run this command to make it all happen.\n\n$ bump overlay openapi.yaml overlays.yaml &gt; openapi.public.yaml\n\n\nNow in openapi.public.yaml I can see my handy new description.\n\n# openapi.yaml\nopenapi: 3.1.0\ninfo:\n  title: Tree Tracker API\n  description: &gt;-\n    Protect Earth's Tree Tracker API will let you see what we've been planting\n    and restoring all around the UK, and help support our work by directly\n    funding the trees we plant or the sites we restore.\n\n    To get involved [contact us and ask for an access\n    token](https://protect.earth/contact) then [check out the API\n    documentation](https://protect.earth/api).\n\n\nThere, that’s a lot more useful!\n\nA pretty simple example, but hopefully an illustrative one, and the use cases can go as far as your creativity. Let’s try some more out.\n\nExample: Updating Public Contact Info\n\nPerhaps your API dev teams are putting their own work emails into the contacts. That is great for internal usage, but you probably don’t want the entire world emailing that one developer. Let’s swap info.contact for a more generic email:\n\n# overlays.yaml\noverlay: 1.0.0\ninfo:\n  title: Overlay to customise API for Protect Earth\n  version: 0.0.1\nactions:\n  - target: '$.info'\n    description: Let's have the public contact general support instead of whoever happened to release this API.\n    update:\n      contact:\n        name: Protect Earth Support\n        url: https://protect.earth/contact\n        email: help@protect.earth\n\n\nThis has done more than just update a string, this has replaced all these properties in an object with the new properties. This can be used to append more properties onto an object too.\n\nExample: Adding Descriptions to Tags\n\nDevelopers not adding descriptions for tags? They’re a great place to put a lot more information about what an “Order” or “Organization” is in the context of this API, so let’s expand on that.\n\n# overlays.yaml\noverlay: 1.0.0\ninfo:\n  title: Overlay to customise API for Protect Earth\n  version: 0.0.1\nactions:\n  - target: '$.tags[?(@.name==\"Order\")]'\n    description: Provide more information for Order tags.\n    update:\n      description: &gt;\n        The Order resource represents a single order for trees, which can be fulfilled by one or more\n        deliveries. Orders are created by the [Protect Earth team](https://protect.earth/contact) and\n        are used to track the progress of your order from creation to delivery.\n\n  - target: '$.tags[?(@.name==\"Organization\")]'\n    description: Provide more information for Order tags.\n    update:\n      description: &gt;\n        The Organization resource represents a single organization, which can be a charity, business,\n        or other entity. Organizations are created by the [Protect Earth team](https://protect.earth/contact)\n        and are connected to each of your Orders.\n\n\n\n  Learn more about working with JSONPath in our guide How to work with JSONPath.\n\n\nDeploy Overlay Changes to Bump.sh\n\nHow can you automate working with overlays? Same idea as working with openapi-filter but we’ll tweak the GitHub Actions a bit to use the Bump.sh CLI update with overlay support.\n\n# .github/workflows/bump.yml\nname: Deploy API documentation\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  deploy-doc:\n    if: ${{ github.event_name == 'push' }}\n    name: Deploy API documentation on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Apply Overlays to customise OpenAPI\n        working-directory: ./api\n        run: |\n          npx bump-cli overlay openapi.yaml overlays.yaml &gt; openapi.public.yaml\n\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: 68ac0647-184a-4e9d-accc-682a5b1f7189\n          token: ${{secrets.BUMP_TOKEN}}\n          file: api/openapi.public.yaml\n\n\nExample: Removing Development Servers with Overlays\n\nBefore you send OpenAPI-based API documentation out to the public you may want to remove development and staging servers to avoid confusing end-users, and in the past that’s been something that has caused conflict with dev teams. Do you force them to remove it from their OpenAPI, despite it being useful for them, or do you try and hack it later?\n\nNo more arguments, and no more hacks. You can do that with overlays.\n\n# overlays.yaml\noverlay: 1.0.0\ninfo:\n  title: Hide Development/Generic Servers\n  version: 0.0.1\nactions:\n  - target: '$.servers.*'\n    description: Remove all other servers so we can add our own.\n    remove: true\n\n  - target: '$.servers'\n    description: Pop our server into the now empty server array.\n    update:\n      - description: Production\n        url: https://api.protect.earth/\n\n\nThis removed all the host entries and added a new production server value in there, so all the generic or development nonsense has been removed, without having to ask developers to clear out servers they might be using in various tools throughout their workflow.\n\nAnother approach:\n\n# overlays.yaml\noverlay: 1.0.0\ninfo:\n  title: Remove Development and Staging Servers\n  version: 0.0.1\nactions:\n  - target: '$.servers[?(@.description==\"Development\" || @.description==\"Staging\")]'\n    description: Remove Development and Staging servers but leave anything else.\n    remove: true\n\n\nThis action will remove any servers with a description of “Development” or “Staging”. This approach could also be done with openapi-filters if you pop x-internal on there, so there is a bit of crossover in what can be done.\n\nSummary\n\nFilters are a quick and relatively simple way to customize OpenAPI, but they can really only do the one thing. If you just want to remove some operations, tags, servers, etc., then openapi-filter is probably the way to go.\n\nOverlays are a lot more advanced and can do both removals and updates. It’s a bit harder to work with, but the power is incredible.\n\nBeing able to change things however you like, then publish the changed versions off seamlessly is really handy, and will hopefully be the last time you need to do awkward JSON/YAML hacking on other peoples documents. JSONPath is a tricky thing to learn, but if you can master regex you can master JSONPath, then the world is your oyster."
        },
        {
          "id": "guides-openapi-code-first-rails",
          "title": "Generating OpenAPI docs for Ruby on Rails with RSwag",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "OpenAPI",
          "tags": "",
          "url": "/guides/openapi/code-first-rails/",
          "content": "API Code-first is the art of building an API, and then popping some annotations or metadata in there to output API documentation in an API description format like OpenAPI.\n\nThe most popular API Code-first approach in Ruby on Rails uses a tool called RSwag.\nWith RSwag you write the OpenAPI for each API endpoint into special tests, which help to confirm your responses are matching the OpenAPI you’ve just written.\n\nCreating OpenAPI with RSwag\n\nThis guide assumes you want to use RSpec as your testing framework in your application.\n\nStep 1: Add the rswag dependencies to the Gemfile.\n\n# Gemfile\ngem 'rspec-rails'\ngem 'rswag'\n\n\nStep 2: Install the new dependencies and run the rswag generator.\n\nbundle install\n\nYou’ll need to run the rspec generator if it’s the first time you use rspec.\nrails generate rspec:install\n\nThen run the rswag generator.\nrails generate rswag:install\n\n\nStep 3: You’ll need some controllers/models to describe, which your application probably already has. If you need to make some, use the following scaffold command to add everything Rails related.\n\nrails generate scaffold Widget name:string\n\nrails db:migrate\n\n\nStep 4: With controllers and models in the app, we can use the rspec:swagger generator referencing a valid controller class name to make an OpenAPI test. _If you’re wondering why its called “Swagger”, that’s the old name for OpenAPI, and with some tools it’s just stuck.\n\nrails generate rspec:swagger WidgetsController\n\n\nStep 5: This will create spec/requests/widgets_spec.rb which will look like this:\n\nrequire 'swagger_helper'\n\nRSpec.describe 'widgets', type: :request do\n\n  path '/widgets' do\n\n    get('list widgets') do\n      response(200, 'successful') do\n\n        after do |example|\n          example.metadata[:response][:content] = {\n            'application/json' =&gt; {\n              example: JSON.parse(response.body, symbolize_names: true)\n            }\n          }\n        end\n        run_test!\n      end\n    end\n\n    post('create widget') do\n      response(200, 'successful') do\n\n        after do |example|\n          example.metadata[:response][:content] = {\n            'application/json' =&gt; {\n              example: JSON.parse(response.body, symbolize_names: true)\n            }\n          }\n        end\n        run_test!\n      end\n    end\n  end\n\n  path '/widgets/{id}' do\n    # You'll want to customize the parameter types...\n    parameter name: 'id', in: :path, type: :string, description: 'id'\n\n    get('show widget') do\n      response(200, 'successful') do\n        let(:id) { '123' }\n\n        after do |example|\n          example.metadata[:response][:content] = {\n            'application/json' =&gt; {\n              example: JSON.parse(response.body, symbolize_names: true)\n            }\n          }\n        end\n        run_test!\n      end\n    end\n\n    put('update widget') do\n      response(200, 'successful') do\n        let(:id) { '123' }\n\n        after do |example|\n          example.metadata[:response][:content] = {\n            'application/json' =&gt; {\n              example: JSON.parse(response.body, symbolize_names: true)\n            }\n          }\n        end\n        run_test!\n      end\n    end\n\n    # snipped patch and delete for readability\nend\n\n\nStep 6: All of this test is not just checking the code is doing what we expect, but it’s actually able to produce OpenAPI for us. Let’s see what we’ve got so far.\n\n$ rake rswag\n\nGenerating Swagger docs ...\nSwagger doc generated at /Users/phil/src/rails-code-first/swagger/v1/swagger.yaml\n\n\nStep 7: OpenAPI being generated means we can deploy it to Bump.sh.\n\n\n  Deploying OpenAPI documentation to Bump.sh.\n  \n    Create and name your first API documentation.\n    Then, retrieve the name and token of this documentation from the CI deployment settings page.\n  \n\n\nStep 8: Install the Bump.sh CLI with npm.\n\nnpm install -g bump-cli\n\n\nStep 9: The moment you’ve been waiting for, deploying your OpenAPI to Bump.sh to generated beautiful hosted documentation!\n\n$ bump deploy swagger/v1/swagger.yaml \\\n  --doc my-documentation-name \\\n  --token my-documentation-token\n\n* Your new documentation version will soon be ready at https://bump.sh/bump-examples/hub/code-samples/doc/rails-hello-openapi\n\n\nStep 10: Head over to your documentation and see how it looks at this early stage.\n\n\n\nIt looks like a start, but it’s missing a whole lot of the what, and the why, which is crucial to making API documentation useful, relevant, and readable. Time to learn a bit more about the RSwag DSL and get more data into our docs.\n\nStep 11: Going back to spec/requests/widgets_spec.rb we leverage the DSL to improve our tests, and improve our OpenAPI. Lets add some headers, a schema to explain how the object is going to look, and a few error responses.\n\n  path '/widgets' do\n\n  get 'list widgets'  do\n    produces 'application/json'\n\n    response 200, 'successful' do\n      header 'Cache-Control', schema: { type: :string }, description: &lt;&lt;~HEADER\n        This header declares the cacheability of the content so you can skip repeating requests.\n\n        Values can be `max-age`, `must-revalidate` and `private`. It can also combine any of those separated by a comma. E.g. `Cache-Control: max-age=604800, must-revalidate`\n      HEADER\n\n      schema type: 'array',\n        items: {\n          type: 'object',\n          properties: {\n            id: {\n              type: 'string',\n              format: 'uuid',\n              example: '123e4567-e89b-12d3-a456-426614174000',\n            },\n            title: {\n              type: 'string',\n              example: 'Neuralyzer',\n            },\n            created_at: {\n              type: 'string',\n              format: 'date-time',\n            },\n            updated_at: {\n              type: 'string',\n              format: 'date-time',\n            },\n          }\n        }\n\n      example 'application/json', :example_key, [\n        {\n          id: 1,\n          title: 'Neuralyzer',\n        }\n      ]\n      run_test!\n    end\n\n    response 429, 'too many requests' do\n      header 'X-Rate-Limit-Limit', schema: { type: :integer }, description: 'The number of allowed requests in the current period'\n      header 'X-Rate-Limit-Remaining', schema: { type: :integer }, description: 'The number of remaining requests in the current period'\n      header 'X-Rate-Limit-Reset', schema: { type: :integer }, description: 'The number of seconds left in the current period'\n\n      run_test!\n    end\n  end\n\n\nThis is a lot, but let’s walk through some of those changes.\n\nThe produces: application/json explains that the output is JSON, and seems to fix a bug in rswag where a lot of other OpenAPI won’t show up.\n\nThen in the 200 response we’ve described headers, including this one for cache controls so clients know they can use client caching middleware if they want to cut down on wasteful repeat requests. Please note we have used a multiline text to describe the header in the OpenAPI document and that it supports Markdown.\n\nresponse 200, 'successful' do\n  header 'Cache-Control', schema: { type: :string }, description: &lt;&lt;~HEADER\n    This header declares the cacheability of the content so you can skip repeating requests.\n\n    Values can be `max-age`, `must-revalidate` and `private`. It can also combine any of those separated by a comma. E.g. `Cache-Control: max-age=604800, must-revalidate`\n  HEADER\n\n\nThe schema block is OpenAPI, but written in Ruby syntax instead of JSON or YAML so we can cut down on some of the parenthesis. This explains what properties can be expected in the response body, and what format they’ll use (UUID, date time instead of unix, etc.) You can even provide an example for the property, to help Bump automatically construct an example for the whole response body to avoid needing to do that yourself.\n\nschema type: 'array',\n  items: {\n    type: 'object',\n    properties: {\n      id: {\n        type: 'string',\n        format: 'uuid',\n        example: '123e4567-e89b-12d3-a456-426614174000',\n      },\n      title: {\n        type: 'string',\n        example: 'Neuralyzer',\n      },\n      created_at: {\n        type: 'string',\n        format: 'date-time',\n      },\n      updated_at: {\n        type: 'string',\n        format: 'date-time',\n      },\n    }\n  }\n\n\nTry making similar changes yourself and running rake rswag again to update swagger/v1/swagger.yaml. Then either use bump deploy to update your hosted documentation, or bump preview --live --open swagger/v1/swagger.yaml to see how it looks without deploying it everytime your make a change in your spec file.\n\nThe RSwag DSL documentation can help you with all sorts of improvements, including creating examples, adding security schemes, and using $ref to reduce repetition in your OpenAPI.\n\nSample Code\n\nThe sample code for this guide is published on GitHub so you can try that if you’re having trouble adding it to your application: rails-code-first, and the deployed documentation is over here.\n\nHonorable Mentions\n\nIf rswag is not working out for you, take a look at some of these tools.\n\n\n  ZRO: OpenAPI Generator for Rails\n  rspec-openapi"
        },
        {
          "id": "guides-openapi-code-first-laravel",
          "title": "Generating OpenAPI docs for Laravel with Swagger-PHP",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "OpenAPI",
          "tags": "",
          "url": "/guides/openapi/code-first-laravel/",
          "content": "API Code-first is the art of building an API, and then popping some annotations or metadata in there to output API documentation in an API description format like OpenAPI.\n\nThe most popular API Code-first approach in Laravel uses a tool called Swagger-PHP. With Swagger-PHP you write the OpenAPI for each API endpoint as annotations or PHP attributes, keeping the API description close to the code that it’s describing.\n\nCreating OpenAPI with Swagger-PHP\n\nStep 1: Install the zircote/swagger-php Composer dependency.\n\ncomposer require zircote/swagger-php\n\n\nStep 2: Add some annotations to your controllers.\n\nSprinkle metadata around your controllers explaining their paths, responses, and adding some descriptions that will help people understand how things work when the documentation is built.\n\n&lt;?php\n\nnamespace App\\Http\\Controllers\\Api;\n\nuse App\\Http\\Controllers\\Controller;\nuse App\\Http\\Resources\\{WidgetCollection, WidgetResource};\nuse App\\Models\\Widget;\nuse Illuminate\\Http\\{Request, Response};\n\nuse OpenApi\\Attributes as OA;\n\nclass WidgetController extends Controller\n{\n    #[OA\\Get(path: '/api/widgets', description: 'Display a collection of widgets.')]\n    #[OA\\Response(response: Response::HTTP_OK, description: 'OK')]\n    public function index()\n    {\n        $widgets = Widget::all();\n        return new WidgetCollection($widgets);\n    }\n\n    #[OA\\Get(path: '/api/widgets/{id}', description: 'Display the specified widget.')]\n    #[OA\\Response(response: Response::HTTP_OK, description: 'OK')]\n    public function show(Widget $widget)\n    {\n        return new WidgetResource($widget);\n    }\n\n    #[OA\\Post(path: '/api/widgets', description: 'Created a new widget.')]\n    #[OA\\Response(response: Response::HTTP_CREATED, description: 'Created')]\n    public function store(Request $request)\n    {\n        $widget = Widget::create([\n            'name' =&gt; $request-&gt;post('name'),\n            'description' =&gt; $request-&gt;post('description'),\n        ]);\n\n        return response()-&gt;json(new WidgetResource($widget), Response::HTTP_CREATED);\n    }\n\n    #[OA\\Put(path: '/api/widgets/{id}', description: 'Update the specified widget by replacing all properties.')]\n    #[OA\\Response(response: Response::HTTP_OK, description: 'OK')]\n    public function update(Request $request, Widget $widget)\n    {\n        $widget-&gt;update([\n            'name' =&gt; $request-&gt;post('name'),\n            'description' =&gt; $request-&gt;post('description'),\n        ]);\n        return response()-&gt;json(new WidgetResource($widget), Response::HTTP_OK);\n    }\n\n    #[OA\\Delete(path: '/api/widgets/{id}', description: 'Delete the specified widget entirely.')]\n    #[OA\\Response(response: Response::HTTP_NO_CONTENT, description: 'Success')]\n    public function destroy(Widget $widget)\n    {\n        $widget-&gt;delete();\n        return response()-&gt;noContent(Response::HTTP_NO_CONTENT);\n    }\n}\n\n\nThis metadata uses the PHP 8 Attributes syntax, but there is a docblock based syntax if you’re stuck on an older version of PHP.\n\nStep 3: Export the OpenAPI from your source code.\n\n$ vendor/bin/openapi src -o openapi.yaml\n\n\nThis outputs a good chunk of OpenAPI for you, so lets take a look and sew what we’ve got.\n\n# openapi.yaml\nopenapi: 3.0.0\ninfo:\n  title: 'My Widget API'\n  version: 1.0.0\npaths:\n  /api/:\n    get:\n      description: 'The home resource shows you what can be done with the API.'\n      operationId: 14776015d6f5a2e7b4d4eb64c7ae2f1f\n      responses:\n        '200':\n          description: OK\n  /api/widgets:\n    get:\n      description: 'Display a collection of widgets.'\n      operationId: 3f6fb451248ef3559606c43b021abfcd\n      responses:\n        '200':\n          description: OK\n    post:\n      description: 'Created a new widget.'\n      operationId: 87fc629a5099009546b979bfbcb47dd2\n      responses:\n        '201':\n          description: Created\n  '/api/widgets/{id}':\n    get:\n      description: 'Display the specified widget.'\n      operationId: 12dd67833d3a2d1036437661a4cd8f07\n      responses:\n        '200':\n          description: OK\n    put:\n      description: 'Update the specified widget by replacing all properties.'\n      operationId: 256916253053a8ae669607bc6cc61e63\n      responses:\n        '200':\n          description: OK\n    delete:\n      description: 'Delete the specified widget entirely.'\n      operationId: b1915b538ae8a9a211fe476ed8219673\n      responses:\n        '204':\n          description: Success\n\n\nStep 4: OpenAPI being generated means we can deploy it to Bump.sh. # Deploying OpenAPI documentation to Bump.sh. Create and name your first API documentation. Then, retrieve the name and token of this documentation from the CI deployment settings page.\n\nStep 5: Install the Bump.sh CLI with npm.\n\nnpm install -g bump-cli\n\n\nStep 6: The moment you’ve been waiting for, deploying your OpenAPI to Bump.sh to generated beautiful hosted documentation!\n\n$ bump deploy openapi.yaml \\\n  --doc laravel-code-first \\\n  --token my-documentation-token\n\n* Your new documentation version will soon be ready at https://bump.sh/bump-examples/hub/code-samples/doc/laravel-code-first\n\n\nStep 7: Head over to your documentation and see how it looks at this early stage.\n\n\n\nIt looks like a start, but there is so much more we can do, including explaining responses, including longer descriptions, all of which is crucial to making API documentation useful, relevant, and readable. Time to learn a bit more about Swagger-PHP Annotations and get more data into our docs.\n\nFor the next trick, let’s add parameters and multiple responses to this controller method.\n\n# app/Http/Controllers/Api/WidgetController.php\n\n#[OA\\Get(path: '/api/widgets/{id}', description: 'Display the specified widget.')]\n#[OA\\Parameter(in: \"path\", name: \"id\", required: true, schema: new OA\\Schema(type: 'string'))]\n#[OA\\Response(response: Response::HTTP_OK, description: 'OK')]\n#[OA\\Response(response: Response::HTTP_NOT_FOUND, description: 'Not found')]\npublic function show(Widget $widget)\n{\n    return new WidgetResource($widget);\n}\n\n\nThis will improve our generated OpenAPI:\n\n  '/api/widgets/{id}':\n    get:\n      description: 'Display the specified widget.'\n      operationId: 12dd67833d3a2d1036437661a4cd8f07\n      parameters:\n        -\n          name: id\n          in: path\n          required: true\n          schema:\n            type: string\n      responses:\n        '200':\n          description: OK\n        '404':\n          description: 'Not found'\n\n\nWhat about adding a schema in so we can see what the response body is going to look like?\n\n# app/Http/Resources/WidgetResource.php\n&lt;?php\n\nnamespace App\\Http\\Resources;\n\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Http\\Resources\\Json\\JsonResource;\nuse OpenApi\\Attributes as OA;\n\n#[OA\\Schema()]\nclass WidgetResource extends JsonResource\n{\n    #[OA\\Property(\n        description: 'Unique auto-incrementing ID of the Widget.',\n        readOnly: true,\n    )]\n    protected int $id;\n\n    #[OA\\Property(\n        description: 'The name of a widget in English.',\n    )]\n    protected string $name;\n\n    #[OA\\Property(\n        description: 'Optional description of what the widget does, and how it works.',\n    )]\n    protected ?string $description;\n\n    #[OA\\Property(\n        description: 'Date the widget was created, in ISO 8601 date time.',\n        example: '2024-01-20T09:15:28Z',\n        readOnly: true,\n    )]\n    protected string $created_at;\n\n    #[OA\\Property(\n        description: 'Date the widget was last updated, in ISO 8601 date time.',,\n        readOnly: true,\n    )]\n    protected string $updated_at;\n\n    /**\n     * Transform the resource into an array.\n     *\n     * @return array&lt;string, mixed&gt;\n     */\n    public function toArray(Request $request): array\n    {\n        return [\n            'data' =&gt; [\n                'id' =&gt; $this-&gt;id,\n                'name' =&gt; $this-&gt;name,\n                'description' =&gt; $this-&gt;description,\n                'created_at' =&gt; $this-&gt;created_at,\n                'updated_at' =&gt; $this-&gt;updated_at,\n            ],\n            'links' =&gt; [\n                'self' =&gt; route('widgets.show', ['widget' =&gt; $this-&gt;id]),\n            ],\n        ];\n    }\n}\n\n\nHere the Swagger-PHP attributes are working with the reflection API to look at the name of the property and infer the types, so we don’t need to define those agai. Rerunning ./vendor/bin/openapi app -o openapi.yaml will add the following to openapi.yaml.\n\nopenapi: 3.0.0\ninfo:\n  title: 'My Widget API'\n  version: 1.0.0\npaths:\n  # snip\ncomponents:\n  schemas:\n    WidgetResource:\n      properties:\n        id:\n          description: 'Unique auto-incrementing ID of the Widget.'\n          type: integer\n          readOnly: true\n        name:\n          description: 'The name of a widget in English.'\n          type: string\n        description:\n          description: 'Optional description of what the widget does, and how it works.'\n          type: string\n          nullable: true\n        created_at:\n          description: 'Date the widget was created, in ISO 8601 date time.'\n          type: string\n          readOnly: true\n          example: '2024-01-20T09:15:28Z'\n        updated_at:\n          description: ''\n          type: string\n          readOnly: true\n      type: object\n\n\nGreat, but now we need to join it up with the controller and a full response body.\n\n# app/Http/Controllers/Api/WidgetController.php\n\n  #[OA\\Get(path: '/api/widgets/{id}', description: 'Display the specified widget.')]\n  #[OA\\Parameter(in: \"path\", name: \"id\", required: true, schema: new OA\\Schema(type: 'string'))]\n  #[OA\\Response(\n      response: Response::HTTP_OK,\n      description: 'OK',\n      content: new OA\\JsonContent(ref: \"#/components/schemas/WidgetResource\")\n  )]\n  #[OA\\Response(response: Response::HTTP_NOT_FOUND, description: 'Not found')]\n  public function show(Widget $widget)\n  {\n      return new WidgetResource($widget);\n  }\n\n\nUsing ref this way feels a little funny at first because you’re using a JSON Reference to something in an OpenAPI description you don’t really control, but once you get the contention it makes sense. The \"#/components/schemas/WidgetResource\" name is generated from the class we put the WidgetResource class that contains a #[OA\\Schema()], and together that instructs Swagger-PHP to make shared schema component, and now it’s been referenced in your schema controller the whole thing comes together like this:\n\n  '/api/widgets/{id}':\n    get:\n      description: 'Display the specified widget.'\n      operationId: 12dd67833d3a2d1036437661a4cd8f07\n      parameters:\n        -\n          name: id\n          in: path\n          required: true\n          schema:\n            type: string\n      responses:\n        '200':\n          description: OK\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/WidgetResource'\n        '404':\n          description: 'Not found'\n\n\nThere is a lot more work to be done until 100% of your API is covered, but you can use these concepts to build out most things, and if you need any other help the Swagger-PHP reference documentation can help you out.\n\nSample Code\n\nThe sample code for this guide is published on GitHub so you can try that if you’re having trouble adding it to your application: laravel-code-first, and the deployed documentation is over here."
        },
        {
          "id": "guides-openapi-code-first",
          "title": "Code-first: How to Generate OpenAPI from Code",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "OpenAPI",
          "tags": "",
          "url": "/guides/openapi/code-first/",
          "content": "API Code-first is the art of building an API, and then popping some annotations or metadata in there to output API documentation in an API description format like OpenAPI. There are a few conceptually different ways to do this, with new tools popping up to help make everything easier, so this guide will show you how those different types of tool work.\n\nFor those of you used to the API Code-first here are the three main workflows you should be thinking about going forwards. If you’ve been documenting your APIs entirely manually with some sort of content management system, wiki, or Word Document, then these ideas might save you from that nightmare.\n\n\n  Annotations\n  OpenAPI-aware Frameworks\n  Traffic Sniffing\n  Move to API Design-first\n\n\nAnnotations\n\nThe classic approach to the API Code-first workflow is to use code comments (or some other form of annotations) as extensions or plugins to write OpenAPI mixed in with the code it’s describing.\n\nHere’s how these annotations look in Go:\n\n// @title           Swagger Example API\n// @version         1.0\n// @description     This is a sample server celler server.\n// @termsOfService  http://swagger.io/terms/\n\n// @contact.name   API Support\n// @contact.url    http://www.swagger.io/support\n// @contact.email  support@swagger.io\n\n// @host      localhost:8080\n// @BasePath  /api/v1\n\n// @securityDefinitions.basic  BasicAuth\n\nfunc main() {\n\tr := gin.Default()\n\n\tc := controller.NewController()\n\n\tv1 := r.Group(\"/api/v1\")\n\t{\n\t\taccounts := v1.Group(\"/accounts\")\n\t\t{\n\t\t\taccounts.GET(\":id\", c.ShowAccount)\n\t\t\taccounts.GET(\"\", c.ListAccounts)\n\t\t\taccounts.POST(\"\", c.AddAccount)\n\t\t}\n    //...\n\t}\n}\n\n\nThen the schema level descriptions are mixed in with the code responsible for outputting resources like this:\n\ntype Account struct {\n    ID   int    `json:\"id\" example:\"1\"`\n    Name string `json:\"name\" example:\"account name\"`\n}\n\n\nOnce the API, endpoints, and resources have all the appropriate annotations there is usually some sort of command you can run to get an OpenAPI document out of it, and that machine-readable document can be used to deploy documentation to Bump.sh or wherever your API documentation lives.\n\nswag init --outputTypes yaml\n\nbump deploy swagger.yaml \\\n  --doc my-documentation-name \\\n  --token my-documentation-token\n\n\nThis approach has been popular for years, with the main selling point being the idea that keeping OpenAPI metadata near the code will hopefully mean developers keep it up to date as they work on the code. This is not always the case, which is one of a few reasons this practice is dying out.\n\nThe other is that many of the annotation tools are stuck on older less useful versions of OpenAPI, namely v2.0 instead of v3.0, or the latest and greatest: v3.1.\n\n\n  Go\n    \n      swag (OAS 2.0)\n      go-swagger (OAS 2.0)\n    \n  \n  PHP\n    \n      Swagger PHP (OAS 3.1 &amp; 3.0)\n    \n  \n  ‌Java/Scala\n    \n      Swagger Core (OAS 3.1 &amp; 3.0)\n    \n  \n  C#\n    \n      NSwag (OAS 3.0 &amp; 2.0)\n      Swashbuckle (OAS 2.0)\n    \n  \n  Node.JS\n    \n      swagger-jsdoc (OAS 3.1, 3.0, &amp; 2.0)\n      express-openapi (OAS 3.0, &amp; 2.0)\n    \n  \n  Ruby on Rails\n    \n      rswag (OAS 3.0 &amp; 2.0)\n      OpenAPI-Rails (OAS 2.0)\n    \n  \n  Python\n    \n      drf-spectacular (OAS 3.1 &amp; 3.0)\n      Django-REST-Swagger (OAS 2.0, abandoned)\n      Flask-RESTplus (OAS 2.0, abandoned)\n    \n  \n  Spring\n    \n      SpringFox (OAS 3.0)\n    \n  \n\n\nDepending on your language and framework choices you may or may not have an option for working with modern OpenAPI, but the lack of modern tooling has been a driving force in people giving up on this approach and looking for alternative workflows. Let’s have a look at some others.\n\nOpenAPI-aware Frameworks\n\nThere’s a new breed of API-centric backend application frameworks popping up which take an exciting approach. Instead of asking you to tack the annotations in around the existing codebase, the frameworks simply produce OpenAPI for you from the actual code you’re writing.\n\nYour application is already declaring routes, defining parameters and incoming validation logic, and helping serialize output. It makes a lot of sense for the framework to help produce this machine readable format for you, from the code you’re already writing.\n\nThere are not as many tools that work this way, but this is likely to be a trend that continues as OpenAPI becomes the dominant API description format.\n\n\n  Go - Huma (Guide)\n  PHP - API Platform (Guide)\n  Python - FastAPI (Guide)\n\n\nJust like annotations you can usually run a command to extract the OpenAPI document, or you can run the web server and pull it down over HTTP.\n\ngo run .\n\nbump deploy http://127.0.0.1:8888/openapi.yaml \\\n  --doc my-documentation-name \\\n  --token my-documentation-token\n\n\nTraffic Sniffing\n\nIf there’s no annotations approach, and you have an existing codebase which cannot be rebuilt with one of these OpenAPI-aware application frameworks, there is another powerful option: sniffing web traffic.\n\nThere’s a whole category of tools popping up, which refer to this functionality as “Recording” or “Learning”. Basically you run an instance of your API somewhere (could be local, test, staging, or even production) and put as much web traffic through it as possible. It will then learn how all the requests and responses look, and produce the best composite OpenAPI that it possibly can.\n\nThis is mostly useful as a one-off, a way to produce a bunch of OpenAPI that you then manage and maintain yourself, because running this forever is not a sensible workflow and will generally only get things 90% right. You need to do the work to get to 100%, but at least you didn’t have to do all of the work of making that initial OpenAPI for an API that already exists. That’s going to be arduous, boring, and likely rife with human error.\n\nCode-first usually needs enhancing \n\nWhether you’re generating from annotations, the framework, or HTTP traffic, there’s a strong chance that you’ll need to put some work in to improve the quality of that OpenAPI. It’s going to be missing long form descriptions, the sort of content that tech writers often produce, and depending on the tool used it’s probably going to be missing examples too. In order to improve this you can use OpenAPI Overlays to enrich the generated OpenAPI with your own logic, and avoid it being overridden the next time OpenAPI is generated.\n\nMove to API Design-first\n\nWith so many of the annotations approaches being outdated, and people usually unable to rebuild an entire codebase to use a framework that happens to emit OpenAPI, a lot of people have given up on the whole code-first approach.\n\nThis is not just an opinion. Searching around the Go community for code-first tooling, most of the “How do I do Code-first in Go” search results show people talking about how they moved to API Design-first and massively prefer the approach.\n\nThe main idea is that instead of writing loads of code and sprinkling in some annotations later to create docs, you create the OpenAPI document before writing any code at all. This is usually in the form of JSON/YAML, but that does not need to be written by hand. There are lots of visual editors to help you build this all up through buttons and forms, with an increasing amount of intelligence to create things. If you’re using VS Code then Copilot is actually incredibly good for instance.\n\nOnce you have the OpenAPI document you can leverage it at every step of the API lifecycle, producing mock APIs for clients to test assumptions with, produce client libraries without writing any code, make really effective contract testing, even generate backend code to get the application teams started once the contract is all signed off. API Design-first is a bit more work up front with a massive payoff in productivity going forwards and forever.\n\nThe main concern with API Design-first is “drift”, where the code and schema diverge over time. The annotations approach only pretended to solve this problem, confusing proximity with accuracy. The comments above code could still completely fail to accurately describe the code below, but nobody would ever notice until a user complained about it.\n\nThe OpenAPI-aware Framework approach does solve this by making the code a single source of truth, but the Design-first approach can be used to make any framework OpenAPI aware, with a source of truth that exists before the code, and continues to be useful after the code is built.\n\nServer-side validation with OpenAPI can avoid the need to write lots of request validation, using middleware to compare incoming HTTP requests against the contract defined in the OpenAPI description and automatically return validation errors instead of having to build that all our yourself.\n\n\n  Java\n    \n      openapi-request-response-validation\n    \n  \n  JavaScript\n    \n      express-openapi-validator\n      fastify-openapi-glue\n      openapi-enforcer\n      openapi-validator-middleware\n    \n  \n  Perl\n    \n      JSONSchema Validator\n    \n  \n  PHP\n    \n      openapi-psr7-validator\n    \n  \n  Ruby\n    \n      openapi_first\n      committee\n    \n  \n\n\nResponses can be validated using any existing test suite, with all popular testing frameworks supporting OpenAPI by extension.\n\n\n  JavaScript\n    \n      chai-openapi-response-validator\n      jest-openapi\n    \n  \n  PHP\n    \n      Spectator\n    \n  \n  Ruby\n    \n      openapi_contracts\n    \n  \n\n\nYears ago the API Design-first workflow was a rough approach, but thankfully a whole bunch of tooling developers spent those years making things excellent, and now it’s easier than ever. Bump.sh adds to that legacy by adding amazing change detection, helping check the OpenAPI in your git repository for changes that would be breaking for end users, letting you know in the pull request when there’s a problem, providing beyond a shadow of a doubt that having your OpenAPI document as a source of truth in a git repository along with your source code is not only handy, but probably the best way to go for many teams."
        },
        {
          "id": "guides-api-basics-api-contracts-extended-introduction",
          "title": "API Contracts - an Extended Introduction",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "API Basics",
          "tags": "",
          "url": "/guides/api-basics/api-contracts-extended-introduction/",
          "content": "An API contract is a document that showcases how an API behaves and how it should be used.\nThis article is all about API contracts: how they can help your business, the best practices to follow, as well as some practical examples.\n\nWhy Are API Contracts Important?\n\nThe purpose of an API contract is to ensure developers using the API can interact with it in a consistent and predictable manner.\n\nA consistent API contract also helps standardize integration with other systems, reducing misunderstandings and errors.\n\nIt’s a useful tool for promoting a shared understanding of your API, easing in changes, and assuring the users of your APIs stability and reliability.\n\nHaving a defined API contract helps streamline processes for both internal and external API users. When multiple external systems or teams are involved, a contract can help avoid misunderstandings and reduce the risk of API usage errors.\n\nIt’s also important in API-first design as downstream teams will often start building mocks of your API before the first version is actually live. Your API development team’s process is also improved with a contract in place, as there’s a clear set of guidelines for how the API should behave.\n\nAPI contracts can contribute to the security and dependability of the systems that use them. By specifying rules around how different systems should interact, API contracts can help prevent misuse of data and ensure that systems can recover gracefully from errors or failures. Good API contracts will also include details about authorization, request limits, and usage restrictions, which helps users avoid accidentally losing access.\n\nFinally, almost any API that uses a strict contracting process will have better documentation than one that doesn’t. Often, API contracts are turned directly into documentation, making the lives of development teams much easier.\n\nWhat Are API Contracts ?\n\nAPI contracts can take many forms, such as a document describing the interface, a formal specification written in a particular language, or a set of code examples illustrating how the API should be used; it can also be written in a combination of formats.\n\nAPI Contracts often follow a preexisting specification like OpenAPI, gRPC, GraphQL, AsyncAPI, or Blueprint.\n\nUsing an established standard helps new developers working with your contract learn it faster and it helps your development team save time on creating and maintaining the contract. Most standard come with a set of tools and a publicly available community which gives them the advantage of almost always being better than coming up with your own API contract from scratch.\nThese contracts typically set out information such as the API’s expected behavior, data formats, authorization and authentication processes, error handling, and limitations. Seeing how the contract changes over time can also help users understand changes to your API as new versions are released.\n\nIt may include details about the specific endpoints (i.e., URLs) that can be called, the parameters that can be passed to those endpoints, and the responses that will be returned. Some API contracts may also include information about authentication and authorization, error handling, and other details that are key to proper integration.\n\nWhat Should You Consider When Designing an API Contract?\n\nHere are some basic concepts to keep in mind while designing your API contract:\n\n\n  \n    Keep it simple, easy to understand, and consistent: In most cases, you should use a predefined specification (as mentioned above) to help users learn and integrate your API into their applications more quickly while reducing the scope for misunderstandings or errors. A uniform vocabulary and style across the contract can also help developers identify what they need and quickly understand how to use the API.\n  \n  \n    Document all parts of the API: This API contract will be a very good place for everyone interacting with your API to have a complete overview of it. The more it is exhaustive, the more they will be able to fully view and understand the structure of your API. Including elements such as inputs, outputs, error codes, etc. is enormously helpful in the implementation process. Examples are also beneficial as they offer a clear understanding of how the API works and how to incorporate it into applications.\n  \n  \n    Thoroughly test the API against the contract: You should test the implementation of your API against the stated contract before release, and provide means of reporting changes - especially breaking ones - or inconsistencies as the API evolves. This means your contract should be in a format allowing your QA process to read and validate against it. Ideally, the testing process should be automated so your API can be validated during the continuous integration process each time changes are made.\n  \n\n\nAPI Contracts: Best Practices\n\nDefining the Expected Behavior of the API\n\nAt a minimum, your API contract should showcase the expected behavior of your API and its endpoints as well as explore details about how an application will call the API. Here’s an excerpt of an API contract showcasing the API behavior and the data you can get from it:\n\nGET /weather\n\nInput parameters:\n- location (string): The location for which to retrieve weather information (e.g. \"New York, NY\")\n- version (string): The version of the API to use (e.g. \"v1\", \"v2\")\n\nOutput:\n- temperature (float): The current temperature in degrees Fahrenheit\n- condition (string): A description of the current weather conditions (e.g. \"sunny\", \"cloudy\", \"rainy\")\n\nExample usage:\n\nGET /weather?location=New%20York,%20NY&amp;version=v2\n\nOutput:\n{\n  \"temperature\": 72.5,\n  \"condition\": \"sunny\"\n}\n\n\n[Representation of the behavior of the API]\n\nYour contract should always provide details on how your users can utilize your API. This excerpt, for example, specifies that the API can be accessed using a GET request to the /weather endpoint and that it takes an input parameter, location, which is a string representing the location for which to retrieve weather information. The second input parameter, version, is also a string and represents the version of the API the user would like to interact with.\n\nIt also makes clear that the API returns a JSON object with two fields: temperature, which is a float representing the current temperature in degrees Fahrenheit, and condition, which is a string describing the current weather conditions.\n\nSpecifying the Contract and API Version\n\nIt is common, on an API lifecycle, to see changes to the API behavior itself.  As an API grows, changes will be made to the current version of the API. The contract is a good place to communicate to your users and API consumers about the changes in your API and how to keep using your API service after the change applies.\n\nIf you need to make a breaking change or your API grows and changes so much that you have the need to release a new version of it, you will need to create a new contract. Each version of your API should possess its own. And ideally each version of the API has their own documentation.\n\nYour contract should note which API version it services.\nIt should also display the API contract version, which can be different from your API version. For instance, in the OpenAPI spec, an info.version field is used to explicitly state the version or revision of the API contract in use.\n\ninfo:\n  description: \"A simple API example.\"\n  title: \"Example\"\n  version: \"1.2\"\n\n[An example of how to specify the version according to the Open API spec]\n\nAs your API contract and API versions change, you should ensure these are noted in your changelog or release notes. This helps users know when a breaking release is made and what they need to do to keep their implementation working.\n\nDocument Data Formats, Limitations, and Restrictions\n\nYour API contract should accurately document the peculiarities and formats of data that a user might receive from API calls. Data normalization is notoriously difficult, so you want to help your API’s users as much as possible by giving them clear descriptions of return types and input expectations.\n\nIt should also set out the limitations that the user should be aware of as they implement the API — for example, rate limits, availability schedules, and requirements for access to certain data. If these constraints are not clear it can be frustrating for users who suddenly hit unexpected limits.\n\nAPI Contract at the Age of Automation\n\nYour API contract does not only allow you all the things listed above, it’s also a good piece to add in your automations.\n\nA common practice is to have your unit tests results tested against the API contract. You can make your CI process fail if the behavior of the API is not matching the expectations of the contract, and you can also analyze changes (including breaking changes) directly from the CI as well.\n\nWhat’s Next for your API Contract ?\n\nOnce you’ve defined your contract and implemented your API, you can use a tool to create the user-facing documentation. In addition to documentation generation based on API contract documents, Bump.sh also integrates with your CI pipeline to ensure that changes to the API are noted in the documentation upon each release.\n\nIf you’re interested in learning more about API design or development, be sure to check out the Bump.sh blog, or sign up for free to see how Bump.sh can help you generate user documentation more easily."
        },
        {
          "id": "guides-openapi-building-your-api-design-mode",
          "title": "Building your API: Design Mode",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "OpenAPI",
          "tags": "",
          "url": "/guides/openapi/building-your-api-design-mode/",
          "content": "At Bump.sh, we are committed to ensuring that your API documentation is beautiful and always up to date. To demonstrate the power of our tooling, we’re going to walk through the creation of an API (and, more importantly, its documentation). As we create the API and modify it, the power of integrating with Bump.sh will become more and more apparent. And this is why we are here: to make everyone’s life easier: easier for  the development team, the documentation team and - of course - easier for your customers to understand and stay abreast of any changes to your API.\n\nIn this post, we’ll create an OpenAPI specification that we can use as a blueprint for our proposed API. We’ll use the planned API endpoints when we build our API.\n\nDesigning our API\n\nFor our example API, we want to create a Joke API, where privileged users can input new jokes, but anyone can interact with the API to search &amp; retrieve jokes. We’ll also include the concept of upvoting and downvoting jokes.\n\nCreating an OpenAPI specification from scratch may seem daunting, but when we take advantage of available tooling, it isn’t too bad. To spec out our API, we’ll use SwaggerHub.\n\nWhen starting a new API specification, SwaggerHub offers you to start with an existing framework. We’ll use the Simple API Template.\n\n\n\nThis creates an API that tracks Inventory. That’s almost what we need, but rather than tracking objects in an inventory, we want to track jokes. So, with some editing (basically turning all “inventory” references to “joke”) we have a Simple Joke API.\n\nThe Inventor object works great for inventory, but we’ll update the new “joke” object to match the parameters that we want to track for our jokes:\n\nJoke:\n  type: object\n  required:\n    - id\n    - dateAdded\n    - joke\n    - punchline\n    - thumbsUp\n    - thumbsDown\n    - category\n  properties:\n    id:\n      type: string\n      example: ce0b9fbe-7ad8-11eb-9439-0242ac130002\n      description: The id of the Joke. Assigned when added.\n    joke:\n      type: string\n      example: Why did the chicken cross the road?\n      description: The lead-in to the joke.\n    punchline:\n      type: string\n      example: To get to the other side.\n      description: This bit should make you laugh :).\n    category:\n      type: string\n      example: Classic\n      description: The category of the joke.\n    thumbsUp:\n      type: integer\n      example: 2\n      description: Count of upvotes for the joke.\n    thumbsDown:\n      type: integer\n      example: 4\n      description: Count of down votes for the joke.\n    dateAdded:\n      type: string\n      format: date-time\n      example: '2016-08-29T09:12:33.001Z'\n\n\nEach Joke will have an ID and include the date added (search for the newest joke). Of course, a joke API wouldn’t be a joke API without parameters for the Joke and the punchline. Finally, we’ll have integers fields to measure the votes for thumbsUp and thumbsDown, and a category for easy filtering of jokes.\n\nEndpoints\n\nIn our initial design - we’ll have 4 endpoints:\n\n\n  Add a joke\n  Stretch for a joke\n  thumbsUp a joke\n  thumbsDown a Joke.\n\n\nI’ll save you all of the details, because you’ll be able to see the documentation in a moment (and this is just a blueprint - odds are we’ll be making changes as we build &amp; test the product).\n\nFinishing up the API\n\nAfter we make all the changes in SwaggerHub, and there are no issues detected by the SwaggerHub UI, we can export the API as a JSON or YAML file.\n\nAdding our documentation to Bump\n\nNow, let’s upload our documentation into Bump, so we can visualize our API. We’ll create a new documentation, and call it the “Jokes Blueprint”\n\n\n\nNow we can add our Swagger file to the documentation, and when we click “deploy” Bump will create our docs for us.\n\n\n\nChanging views of our API\n\nWhen the API is added, we can immediately see our documentation online:\n\n\n\nOur endpoints are on the left, the description in the middle, and examples on the right. Bump has several settings to change the way your API will be displayed.\n\nYour choices here are:\n\n\n  Group by path\n  Group by tag\n  Default (group by path)\n\n\nGroup by Path\n\nThe group by path just shows 3 endpoints: Joke, ThumbsUp and ThumbsDown. Inside the Joke group, you can see the GET (search jokes) and POST (upload new jokes).\n\n\n\nGroup by tag\n\nGroup by tag uses the tags inside the API. The Joke POST is tagged as Admin (only privileged users can add jokes), while Joke GET, and the voting endpoints are available to all developers:\n\n\n\nIn this view, you cannot see the actual endpoints of the API, but we’ll fix this with the next setting: Navigation\n\nNavigation\n\nThe default Navigation is Groups only.\n\nOther options include:\n\n\n  Groups and Operations\n  Groups and Operations with verbs\n\n\nFor space reasons, we’ll show just the “groups and operations with verbs.” Should you choose the option without verbs, the GET/POST/PATCH etc. verbs will not be shown:\n\n\n\nWith this view, the endpoints are organized under the Admin and Developer tags (Group by Tags). Adding “Operations” places each operation under the tag. As seen above, we can see the “Searches jokes”, “Thumbsup a joke” and “Thumbsdown a joke” under the Developers group.\n\nAdding the verbs tells you that “search jokes” is a GET, and the voting endpoints are both POST.\n\nColors\n\nFinally, once you have your API looking great, you can update the colours to match your company’s colour schemes.\n\nConclusion\n\nIn this post, we’ve begun the process of creating an API. To do so, we’ve created a V0 API blueprint. With this blueprint, we’ve set out the capabilities of our Joke API, which we can use as we build our application.\n\nIn these steps we used API design first to help us understand what it is we wanted to build. We don’t expect our final API to be a 100% match this design, but it will serve as a guide to the project as we begin our development work.\n\nFinally, we took our OpenAPI specification and added it to Bump so that we can best understand how to display our API documentation."
        },
        {
          "id": "guides-api-basics-how-to-use-document-polymorphism-api",
          "title": "How to use and document polymorphism in APIs",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "API Basics",
          "tags": "",
          "url": "/guides/api-basics/how-to-use-document-polymorphism-API/",
          "content": "Don’t Repeat Yourself. Be DRY! Anyone who ever had to manually correct thousand of flyers printed for an event,\nwith an obvious typo in date or location, could confirm it.\nIt would have been easier to review the original file before running the presses.\n\nEvery developer knows this rule (or should, at least).\n\n\n  Asserting that code should be easy to change is akin to stating that children\nshould be polite; the statement is impossible to disagree with.\n(Source: Practical Object-Oriented design, Sandi Metz)\n\n\nAs described in this very interesting book, easy to change code\ncan be defined with:\nsmall changes in requirements require correspondingly small changes in code.\n\nAnd to do so, Single Responsibility, Don’t Repeat Yourself and Single Source of Truth\nare very important design patterns to develop smart applications, easy to use, easy to develop, easy to maintain, and thus improve the Developer Experience.\n\nOn this page, I’ll do my best to explain and illustrate how polymorphism can be used in API development to help achieve these big objectives.\nAnd I’m glad to share with this post a synthesis of what I learned for months about polymorphism, composition and inheritance support in API documentations, based on OpenAPI and AsyncAPI specifications.\n\nPolymorphism concept\n\nWe don’t want to have two different behaviors for very similar objects, some parts of code\nand logic would be duplicated.\nPoint about polymorphism is to extract what is common by nature, and what is specific to this instance.\n\nFor the following, we’ll need an example to illustrate all these situations.\n\nLet’s consider a real estate agency application, whose purpose is to offer accommodations for rent.\nWe can consider that the main object for this agency is accommodation.\n\nBut what type of accommodation?\n\n\n  House, with a garden, a tiled roof, a total size, monthly rent and an address.\n  Apartment, with a specific floor, maybe parking slots and elevator, a total size, monthly rent and an address.\n\n\nWait… Déjà-vu, right?\n\nHouse and Apartment have many shared informations, that are related to what they are by nature:\nA place where people live by giving some money.\n\nIndeed, both house and apartment have shared information: a monthly rent, an address,\ninformation about total area.\n\nBut only an apartment has a specific floor. And we don’t usually care about\nroof tiles unless you live in a house.\n\n\n\nThere are many solutions for implementing that into your codebase,\nbased on your team, language, framework, architecture, opinions about typed VS object oriented language…\n\nBut as said someone wise about API:\n\n“Do what the hell you want with your backend, but expose a simple and documented API”\n\nPolymorphism in API\n\nPolymorphism in API refers to the possibility to use the same endpoint for\nsimilar but different objects. Indeed, it’s a good practice\nto avoid different endpoints to do somehow the same action.\n\nFor example, in a request body, we should favor a single endpoint to handle requests for different data types or structures.\nAnd for a Response, it’s practical to allow a single endpoint to return different representations of a resource, based on the request made by the client.\n\nThis gives a more flexible and scalable API design, as well as reducing the amount of endpoint duplications.\n\nIn our example, without polymorphism, we could imagine two different endpoints to add\na new accommodation to the real estate agency database.\n\n\n  POST /house\n  POST /apartment\n\n\nBoth would require a request body, with a lot of fields regarding house or apartment.\nBut we know that there are some shared information between these two object,\nbecause they have something in common by nature: both are a place to live for (at least) humans.\n\nA good practice here is to expose only one endpoint for this:\n\n  POST /accommodation\n\n\nRegarding the data provided to the request body, result would be to create\neither a House, or an Apartment.\n\nThe request body for creating an apartment might look like this:\n\n{\n  \"type\": \"apartment\",\n  \"size\": 17,\n  \"rent\": 707,\n  \"address\": \"rue de Clery, 75002 Paris, France\",\n  \"floor\": 4,\n  \"elevator\": true\n}\n\n\nWhile the request body for creating a house might look like that:\n\n{\n  \"type\": \"house\",\n  \"size\": 149,\n  \"rent\": 500,\n  \"address\": \"chemin du haut Pertuzou, 38160 Saint-Verand, France\",\n  \"garden_size\": 5000,\n  \"roof_tiles_type\": \"Clay\",\n  \"solar_panels_power\": 800\n}\n\n\nWe can see here property type is used to find out what kind of accommodation has to be created.\nIt can be very well documented for REST or Event-Driven APIs, we’ll come to this later.\n\nIn this way, polymorphism in REST APIs allows for a more flexible and scalable API design, as well as reducing the amount of endpoint duplications.\n\nBut API is consumed. And API consumers have to be very aware of this polymorphism.\nAt Bump.sh, we are convinced that the best solution is to have a very nice documentation for your API.\n\n\n\nsource: https://bump.sh/demo/hub/support/doc/accommodation-polymorphism\n\nNow, let’s see how to properly document polymorphic resources with OpenAPI\nor AsyncAPI specifications, based on JSON Schema.\n\nPolymorphism in API documentation\n\nJSON schema\n\nPolymorphism is natively supported by JSON Schema, to handle schema composition.\n\nAs described in JSON schema documentation\n\n\n  JSON Schema includes a few keywords for combining schemas together (…)\nThe keywords used to combine schemas are:\n  \n    allOf: (AND) Must be valid against all of the subschemas\n    anyOf: (OR) Must be valid against any of the subschemas\n    oneOf: (XOR) Must be valid against exactly one of the subschemas\n  \n\n\nFirst combinator allOf is not really concerned by this article.\nIndeed, in our example, it’s difficult to imagine an accommodation being\nboth a house and an apartment. Currently, it can be summarized as a merge\nbetween schemas described behind allOf list.\n\nWhat is concerned by polymorphism are alternatives, anyOf (OR) or oneOf (XOR).\n\nNow let’s see how to document alternatives, with JSON schema, in OpenAPI and AsyncAPI\nspecifications. Good news, since both are based on JSON schema, same rules\ncan be applied (except about discriminator, we’ll handle it very soon).\n\nWe could imagine an OpenAPI request body, or response content,\nor even AsyncAPI message payload or headers, each schema is described with a\nSchema Object. Alternatives could be inserted in a lot of places:\n\n# OpenAPI\npaths:\n  /accommodations:\n    post:\n      summary: Create an accommodation\n      requestBody:\n        content:\n          application/json:\n            schema:\n              # insert schemas alternatives here\n      responses:\n        200:\n          description: Accommodation has been created.\n          content:\n            application/json:\n              schema:\n                # insert schemas alternatives here\n\n# AsyncAPI\nchannels:\n  accommodations:\n    subscribe:\n      description: Accommodation creation\n      message:\n        payload:\n          # insert schemas alternatives here\n        headers:\n          # insert schemas alternatives here\n\n\nNext, we’ll consider the OpenAPI request body to create an accommodation.\nEither house, either apartment.\n\nOpenAPI example\n\nPlease see below the complete example of POST /accommodation request body,\nused for this documentation.\n\npaths:\n  /accommodation:\n    post:\n      summary: Create an accommodation\n      description: |\n        Example about polymorphism in API.\n        This endpoint 'POST /accommodation' can be used to\n        create either apartment, either house.\n      requestBody:\n        content:\n          application/json:\n            schema:\n              oneOf:\n                - $ref: \"#/components/schemas/House\"\n                - $ref: \"#/components/schemas/Apartment\"\n\ncomponents:\n  schemas:\n    Accommodation:\n      type: object\n      required:\n        - type\n        - size\n        - monthly_rent\n        - address\n      properties:\n        type:\n          type: string\n        size:\n          type: integer\n        monthly_rent:\n          type: number\n        address:\n          type: string\n    Apartment:\n      allOf:\n        - $ref: \"#/components/schemas/Accommodation\"\n        - type: object\n          required:\n            - floor\n          properties:\n            floor:\n              type: integer\n            collective_heating_system:\n              type: boolean\n            elevator:\n              type: boolean\n            parking_spots:\n              type: integer\n    House:\n      allOf:\n        - $ref: \"#/components/schemas/Accommodation\"\n        - type: object\n          properties:\n            garden_size:\n              type: integer\n              description: Area, in square meters (m²).\n            roof_tiles_type:\n              type: string\n              description: Multiple types exist for roof tiles.\n            solar_panels_power:\n              type: integer\n              description: Installed photovoltaic power, in Watt (Wc). _Yes, modern house should have solar panels_\n            basement_size:\n              type: integer\n\n\n\nCombinators anyOf or oneOf have to be used at first level,\nwith an array of related schemas, here House and Apartment.\n\nInheritance &amp; DRY\n\nHere, there are three different schemas listed in /components/schemas/* list:\n\n\n  Accommodation, with every properties shared by House and Apartment, as accommodation.\n  Apartment,  with every properties specific to an apartment\n  House,  with every properties specific to a house.\n\n\nIndeed, we know that both House and Apartment share a lot of details, by inheriting\nfrom parent object Accommodation.\n\nWe can see it in each schema House and Apartment, with the merging combinator\nallOf.\n\nHouse has every properties from Accommodation (type, size, monthly rent, and address),\nand some specific house properties: garden size, roof tiles type or solar panels power…\nAnd same for Apartment.\n\nThis usage of parent object, and allOf combinator is a useful\nsolution to avoid code duplication here, and apply Inheritance pattern in JSON schema.\n\nExtract title\n\nIn both OpenAPI and AsyncAPI documentation, it’s strongly recommended to avoid\ninline schemas, ie to favor schemas described behind an internal reference $ref.\n\nWhat is crucial is the ability to extract a title for each alternative.\nWith $ref, title is defined from last element of path:\n\n\n  $ref: \"#/components/schemas/House\": extracted title is House\n  $ref: \"#/components/schemas/Apartment\": extracted title is Apartment\n\n\nBut we can also favor to extract this title from the explicit title field, if provided in the schema object.\n\nFor example if third and very lightweight alternative accommodation was added\nwithout reference:\n\nrequestBody:\n  content:\n    application/json:\n      schema:\n        oneOf:\n          - $ref: \"#/components/schemas/House\"\n          - $ref: \"#/components/schemas/Apartment\"\n          - type: object\n            title: Yurt\n            required:\n              - type\n            properties:\n              type:\n                type: string\n              weight:\n                type: integer\n\n\n\nThis alternative would be named Yurt.\nIf we had omitted the title: Yurt attribute, Bump.sh would have to generate a title based on type and index\n(here it would be 'object-2')\n\nDiscriminator\n\nNote: for this section, every alternative has to be object, or array of objects,\na schema with properties. If no properties, no discriminator.\n\nNow’s the time to clarify this required type property, used on purpose for\nevery schemas inheriting from Accommodation object.\n\nIt’s a good practice to use a specific property to distinct which schema\nhas to be used.\n\nThis can be considered as a hint for the API. If type is explicit,\nthere is no need for backend to validate if provided data are compliant\nwith House or Apartment . Indeed, it would be weird (and costly)\nto detect garden_size or parking_slots and implicitly guess accommodation type.\n\nBecause this is missing in JSON schema support, both OpenAPI and AsyncAPI\nhave introduced a specific keyword for this, discriminator:\n\ncf documentations:\n\n  AsyncAPI 2.x discriminator (as String)\n  OpenAPI 2.0 discriminator (as String)\n  OpenAPI 3.x discriminator (as Object)\n\n\nFirst solution is to use string format (supported by OpenAPI 2.x, aka Swagger,\nand AsyncAPI).\n\nIn every alternative with properties, one property is identified as the discriminator.\nThis property has to be required, and of course, it has to be shared between all alternatives, so\nwith our example, it’s relevant to use shared Accommodation schema:\n\nAccommodation:\n  type: object\n  discriminator: type\n  required:\n    - type\n    - size\n    - monthly_rent\n    - address\n  properties:\n    type:\n      type: string\n    # …\n\n\nThus, correct alternative is chosen by matching provided value for type\nwith each alternative title:\n\n  House value for type clarifies that House schema will be used\n  Apartment value for type clarifies that Apartment schema will be used\n  Flat value for type clarifies that Apartment schema will… wait, what??\n\n\nYou read it well. Sometime, API maintainers could expect an other value instead of\nalternative title to ensure schemas matching.\n\nAnd that’s why discriminator support is different for OpenAPI 3.1:\n\nIn this case, discriminator is an object, with two fields:\n\n  propertyName, string value of the property used as discriminator\n  mapping, a hash to give explicit values\n\n\nAnd in this case, it has to be defined at the same level as the combinator.\n\nIn our example:\n\nrequestBody:\n  content:\n    application/json:\n      schema:\n        oneOf:\n          - $ref: \"#/components/schemas/House\"\n          - $ref: \"#/components/schemas/Apartment\"\n        discriminator:\n          propertyName: type\n          mapping:\n            house: \"#/components/schemas/House\"\n            flat: \"#/components/schemas/Apartment\"\n\n\nThis is visible in generated documentation, where allowed value is explicitly\ndefined as either flat, either house.\n\n\n\nSee it live\n\nConclusion\n\nPolymorphism and inheritance are essential patterns to improve your API design and\navoid code duplication (no Single Source of Truth when your code is duplicated).\n\nBoth OpenAPI and AsyncAPI have nice tools to support them, you just need to choose oneOf these specifications.\n\nI hope this post/article/guide/tutorial/page (anyOf these formats) did help you on your polymorphism in API journey.\n\nWriting this article was not easy, but I can assure you that implementing all the support for all these polymorphism concept\nin both OpenAPI and AsyncAPI was not a long, quiet river.\nThat’s why I’m very glad to reveal that these combinators anyOf and oneOf are now fully supported in every documentation hosted on Bump.sh, as\ndetailed in our changelog.\n\nHappy polymorphism,\n\nPS: About Single Source of Truth, Sébastien would be very happy if I could also mention here it’s what we do at Bump.sh, help API developers team to have a single source of truth for all their API contracts and avoid duplication… but that’s not the point here, sorry Seb 😉\n\nOh wait… I did it again?"
        },
        {
          "id": "guides-api-basics-what-are-the-different-api-types",
          "title": "What are the different API types?",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "API Basics",
          "tags": "",
          "url": "/guides/api-basics/what-are-the-different-api-types/",
          "content": "APIs have become increasingly popular over the past few years, enabling products, projects and people to connect. In this article, we will try to present a snapshot of the most popular solutions available as of August 2022. We did our best to avoid bias, but some technologies are not mentioned on purpose, as we want to focus on the most used/popular.\n\nHistory\n\nAlways great to start from the beginning. If APIs have been here for a very long time, they began to get more and more popular in the early 2000s, when they got mainly used to push forward businesses on the web. Resellers or business partners could reach popular platforms, with APIs helping customers quickly find the products they were looking for on a single website.\n\nSome of the biggest technology companies released their APIs like Salesforce or Amazon in a few years. Their impact on the industry is unmatched, as they forever changed how we sell and shop online.\n\nWe could continue this for a long time. After shopping, more APIs were released for different purposes and are now everywhere: social media, cloud computing, communication, mapping, voice AI…\n\nRPC (Remote Procedure Call)\n\nThe simplest form of API interaction basically relies on executing some code on a distant server. RPC is probably the oldest type of API you could meet today. You can imagine it as a simple function with arguments but in a web context that makes them a web API.\n\nRPC was designed for actions, executing procedures and commands with ease. One of the limitations is that it relies on the client mostly, which needs to know the endpoints and how and when to reach them. RPC itself is more an approach to APIs but with many existing specifications. We won’t talk about most of them but there’s one that is popular:\n\nSOAP\n\nIntroduced by Microsoft and IBM around 1998, SOAP is an actual communication protocol. Known to use a bit wordy XML, SOAP is from the beginning a true API contract: its strict guidelines confer it an image of stability and safety. Today, SOAP is still found on some legacy systems which rely on it, but has mostly been supplanted by REST. Among the companies that use SOAP for their APIs, Salesforce is probably the most famous.\n\nLearn more on SOAP.\n\nMore RPC types\n\nRPC has known some evolutions since its creation. One of them led directly to SOAP: XML-RPC (basically, it continued to evolve until the decision to create a new standard from it). JSON-RPC has also been a bit popular at some point. Its main difference from XML-RPC is its capacity to handle notifications and work asynchronously.\n\nREST\n\nREST APIs quickly grew in popularity, pushing SOAP on their passage. While RPC is a strict protocol, REST can be seen as general guidelines which define a standard, normalized architecture for APIs.\n\nDue to this, REST offers fast deployments in your environment. With URLs for the client/server relations, REST avoids the time-consuming part of writing code for each. Client and server can be developed on their side without information about each other, and their code can be updated without affecting the other.\n\nAnother important part of REST is its Statelessness: to make it simple, it means that clients and servers don’t need to know the state of the other one. REST uses standard HTTP operations and doesn’t need to code a specific interface. Messages sent are automatically understood, without knowing the previous messages sent.\n\nREST supports webhooks enabling asynchronous requests. Imagine subscribing to an alert when a specific event occurs or when you need a reply/action from the server at a later moment.\n\nThere are many ways to describe your REST API but one of the most popular solutions lately has been to use the OpenAPI specification, previously known as Swagger.\n\nLearn more on REST.\n\nOpenAPI Specification\n\nWhile REST APIs were growing on the main stage, several actors worked hard to push this forward. One of the main issues was standardizing how APIs should be described. Swagger was one of these neutral description formats and eventually joined the Linux Foundation. The name changed as well to OpenAPI. Now the most popular standard to describe REST APIs, its community greatly supports OpenAPI specification.\n\nLearn more on OpenAPI.\n\nGraphQL\n\nInitially created by Facebook in 2012, GraphQL was moved as an open-source project to the GraphQL Foundation in late 2018. GraphQL is a query language for APIs, focusing on delivering exactly the data requested and no more. Schema is essential to a GraphQL API, as it is your API’s single source of truth. A single endpoint can share the full capability in terms of data, allowing you to control the data you’ll receive from the server precisely. And when REST APIs could need loading from several endpoints, GraphQL handles this in a single request.\n\nHowever, as many developers are more familiar with REST APIs, GraphQL may require a longer learning curve.\n\nLearn more on GraphQL.\n\ngRPC\n\nCreated by Google, gRPC is an open source RPC framework, cross-platform with many languages, relying on HTTP 2.0 as its transport protocol. Protocol Buffers (also known as Protobuf) can be used for requests/messages. Protobuf could be seen as Google homemade JSON (but faster, smaller and natively generating language bindings).\n\nWe won’t deep dive too much here, but gRPC has some significant advantages like its use of binary payloads (very light requests/code, better performances) or the use of HTTP 2.0 backstage.\n\ngRPC may be considered as a strongly enhanced version of RPC.\n\nLearn more on gRPC.\n\nEDA\n\nUnlike REST (which relies on a request/response workflow), Event-Driven Architecture API (or asynchronous APIs) works with a subscribe/publish system. Asynchronous APIs are based on a message workflow, allowing asynchronous actions. Instead of reaching out to a server to get a response, an EDA API will subscribe and receive specific notifications about an event, when required. In some cases, it could be considered a great solution due to its excellent performance and reliability, like for a heavily requested server.\n\nAsynchronous APIs are very popular in microservices environments, helping them to synchronize data across different services.\n\nThey have been growing in popularity over the years, receiving more and more support from the developer community. If asynchronous APIs can work with many protocols, a standard emerged to bind them all within the specification:\n\nSpecifications\n\nAsyncAPI is an open source standard describing an EDA API’s specifications, working with many possible protocols. Based on OpenAPI, AsyncAPI has optimized these parts for asynchronous needs: Designed from the idea of a messaging system where the messages are built with a header and a payload, helping to sort them into topics.\n\nAsyncAPI focuses on the application and the communication channels and works well with CloudEvents, another standard for asynchronous APIs that focuses more on the event itself and the message format.\n\nLearn more on AsyncAPI.\n\nMost commonly used protocols\n\nWe’re covering below a few protocols that are popularly used within an EDA API, but there are many more!\n\nAMQP\n\nInitially created by JPMorgan Chase in 2003 to build an open communication standard, AMQP has been supported by some of the biggest: Microsoft, VMWare, Cisco Systems and several banks like Barclays.\n\nAmong others, AMQP’s biggest advantages are its support of annotated data and its self-describing encoding system, which permits greater compatibility with clients.\n\nLearn more on AMQP.\n\nMQTT\n\nMQTT is a bit older than AMQP (1999) and was initially created to watch over an oléoduc in the desert. As the satellite connection was expensive, the goal was to have a bandwidth-efficient, battery-power-efficient protocol.\n\nMQTT never stopped to evolve and is now one of the most popular protocols in the IoT field, thanks to its support of unreliable networks.\n\nLearn more on MQTT.\n\nKafka\n\nKafka is often considered an alternative to “traditional” message brokers. Designed by LinkedIn, Kafka was meant to support the high volume of messages transiting through the platform. It quickly then switched to open-source in 2011, under the Apache Software Foundation.\n\nUber uses Kafka to help match passengers and drivers. More than 300 microservices rely on Kafka now and the protocol is considered the backbone of their architecture.\n\nLearn more on Kafka.\n\nWebSockets\n\nWebSocket protocol has been here for a long time, standardized in 2011, and is supported natively by most browsers. Offering a very flexible implementation, you can easily find many resources online and a solid tooling experience.\n\nWebSockets APIs are stateful (meaning that the connection between client and server remains active until stopped by one of them) and full-duplex (can send/receive simultaneously).\n\nWidely spread in the EDA ecosystems, WebSockets are increasingly partnering with REST APIs to add an asynchronous layer to them.\n\nLearn more on WebSockets.\n\nConclusion\n\nThis article was just a short tour of today’s API scene, as there’s much more to share on this growing, vivid topic. New concepts, ideas, projects, tools, standards, and optimizations are developed and shared weekly within the community. And even if we know that some content in this article will be obsolete in the upcoming months, we’ll try to keep it updated as much as possible.\n\nIn the meantime, we at Bump are trying to create great tools to help developers, API evangelists, communities and teams to get the best from their APIs. We have developed the first product that provides a unified experience around OpenAPI and AsyncAPI. Feel free to look at our solution, and please reach us if you have any feedback, comment or suggestion you would like to share. We’re always listening. :)"
        },
        {
          "id": "guides-asyncapi-what-is-asyncapi",
          "title": "What is AsyncAPI?",
          "collection": {
            "label": "guides",
            "name": "Guides"
          },
          "categories": "AsyncAPI",
          "tags": "",
          "url": "/guides/asyncapi/what-is-asyncapi/",
          "content": "AsyncAPI is the most popular specification for describing asynchronous APIs and Event-Driven Architectures.\nOpen-source and partially based on OpenAPI standards, AsyncAPI has been a solid and efficient answer to asynchronous communication needs, especially since its 2.0 version.\n\nA brief look at APIs’ history\n\nLet’s start by explaining, very simply, APIs.\n\nA long time ago, communication between applications or services required you to deep dive into their code if you had access to it, which could take quite a while if they were many (or messy). APIs have been created to make this part of programming much easier: APIs tell you precisely what you can ask to an application and what to expect in return, without seeing what the application does underneath.\n\nAt first, almost every API had its own way of working and communicating. And if documentation was missing or outdated, it would quickly become a mess. Different standards were built, but one of them ultimately rose to the top: OpenAPI.\n\nOpenAPI (previously called Swagger until version 3.0) has been the most popular choice for REST APIs, solving documentation problems and creating a good standard. Now part of the Linux Foundation, you can learn all about OpenAPI right here, or check out our example Train Travel API.\n\nWhat about asynchronous APIs?\n\nChoosing between a synchronous or an asynchronous API is basically an architectural decision. If classic synchronous APIs are helpful when you expect a response to any request you make, asynchronous APIs can be useful in other situations like telling application that some event occurred without expecting a callback or storing data on a device with connection issues.\n\nFor example, we could think of a chef working in a restaurant. An asynchronous API (here, the waiter) would suit this perfectly as the kitchen just need to get the dishes and the table they belong to. You can even imagine richer messages, including food allergies or meat cooking requests. You do not expect a reply from the kitchen every time.\n\n\n\nHere comes AsyncAPI: built with OpenAPI’s legacy in mind and sharing some concepts with, it uses some parts of its original structure and optimizes them for asynchronous needs.\n\nDefinitions\n\nAsyncAPI specification\n\nConsider this as the guidelines to describe your asynchronous API to ensure that you follow the standards.\n\nAsyncAPI definition\n\naka AsyncAPI file / document / description\n\nThis is the part you produce, explaining exactly how your API works, what it can or cannot do, what data it accepts and returns.\n\nAsyncAPI documentation\n\nThe human-readable documentation of your AsyncAPI definition. It can be automatically generated to save time.\n\nWhat does it look like?\n\nAsyncAPI documents can be written in YAML and JSON. This example will use YAML, as it’s the most popular and readable format. Let’s have a quick look:\n\nasyncapi: 2.3.0\ninfo:\n  title: Hello world application\n  version: '0.1.0'\nservers:\n  production:\n    url: server.cogip.com\n    protocol: amqp\n    description: Absolutely official COGIP server.\nchannels:\n  hello:\n    publish:\n      message:\n        bindings:\n          amqp:\n            contentEncoding: gzip\n            bindingVersion: 0.2.0\n        payload:\n          type: string\n          pattern: '^hello .+$'\n\n\nAsyncAPI document structure\n\n\n  Right after the document type and its version comes the info object, which contains at least the title (the name of your API) and the version (don’t forget to change it after every update).\n  servers tells where and how to connect to your message broker or server. You need to specify a protocol, like amqp, mqtt or ws for instance, and a url.\n  With AsyncAPI, you use channels (like the paths in OpenAPI) to send your message to the application. This section details how messages flow through.\n  The payload is the part where you explain how message are made: types, format, pattern, etc…\n  bindings describe protocol-specific information. It can be defined specifically at the message level or generally at the servers level.\n\n\nAsyncAPI benefits\n\nIf OpenAPI is HTTP and endpoints oriented, AsyncAPI allows many protocols, as we have seen before in the server section of the document, but also more API styles (request/response, publish/subscribe, etc…). AQMP, IBM, Kafka, MQ, MQTT, SNS, WebSockets, JMS for the most popular.\n\nTooling\n\nAsyncAPI has gained more and more popularity since its version 2.0. Great tools are continuously developed by the AsyncAPI community, and here’s a short selection from our favorites:\n\nEditors\n\n\n  AsyncAPI Studio: Still in beta, this tool was expected from the community. Great to start playing with the standard.\n\n\nValidators\n\n\n  AsyncAPI Parser\n  Spectral\n\n\nMocking &amp; Testing\n\n\n  Microrocks\n  Virtualan\n\n\nDocumentation\n\n\n  Bump.sh 💙\n  AsyncAPI Generator\n\n\nAsyncAPI has a fantastic list of valuable tools that make the difference for most use cases, you may want to check it out.\n\nMore resources\n\nWe hope this short tour of AsyncAPI made you want to try it yourself. We strongly suggest giving it a try with the AsyncAPI Studio, or even find some public examples like these ones:\n\n\n  Slack API\n  Gitter-streaming API\n\n\nBump was one of the first SaaS products to believe in and support AsyncAPI since its 2.0 release. We have worked continuously for the past years, convinced that it will become the same strong standard to asynchronous APIs that OpenAPI is to the REST world today.\nWe are proud to be one of AsyncAPI’s Bronze Sponsors and we hope you’ll enjoy coding with AsyncAPIs as much as we do.\n\nIf you have any questions or comments, feel free to reach out. We’re just a mail away from you!"
        },
        {
          "id": "openapi-v3.1-extending-extensions",
          "title": "Specification Extensions in OpenAPI",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/extending/extensions/",
          "content": "Add a feedback link (x-feedbackLink)\n  Add topics to your documentation (x-topics)\n  Custom code sample examples (x-codeSamples)\n  Expose your beta features (x-beta)\n\n\nOpenAPI v3.1 has a concept of Specification Extensions which are additional properties not specified by the OpenAPI specification. These are a chance to customize and integrate tools from documentation to API gateways, all hooking into the OpenAPI document and storing information important to them in the form of extra properties that will be ignored by other tooling.\n\nAll those properties start with the x- naming convention to be identified as “eXternal” from the OpenAPI specification.\n\nAdd a feedback link (x-feedbackLink)\n\nThe x-feedbackLink object can be added directly in the info object of your OpenAPI document. Find out more in our dedicated section.\n\nAdd topics to your documentation (x-topics)\n\nThis vendor-specific property we created helps to add more context paragraphs in your generated documentation. Find out more in our dedicated section.\n\nCustom code sample examples (x-codeSamples)\n\n\n  This vendor extension is only available for OpenAPI documents for now\n\n\nWe added a custom property, not supported by OpenAPI, so you can add your own code samples in one or more programming languages to your documentation. Find out more in our dedicated section.\n\nExpose your beta features (x-beta)\n\nThis custom property allows you to identify some components of your\ndocumentation as beta. Find out more in our dedicated section."
        },
        {
          "id": "openapi-v3.1-extending-overlays",
          "title": "Extending OpenAPI Documents with Overlays",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/extending/overlays/",
          "content": "Adding more context with Overlays    \n      Tags\n      Introductory Topics\n      Code Samples\n      External Documentation\n    \n  \n  Summary\n\n\nHowever you make OpenAPI descriptions for your APIs, there are all sorts of scenarios where you might want to customize it for different audiences. Perhaps your tech writers want to add amazing longer descriptions but they are locked out of the source code, or you want to hide some internal endpoints from your OpenAPI before publishing. Is it possible to do all this without awkwardly managing multiple similar-but-different OpenAPI documents?\n\nThe OpenAPI Initiative have released a new concept called “Overlays”. This is separate specification but compatible with OpenAPI, and while it’s labelled “experimental” its a v1.0.0, with support in a variety of tooling including Bump.sh.\n\n# overlays.yaml\noverlay: 1.0.0\ninfo:\n  title: Improve Descriptions\n  version: 0.0.1\nactions:\n  - target: '$.info.description'\n    description: Provide a better introduction for our end users than this techno babble.\n    update: &gt;-\n      Protect Earth's Tree Tracker API will let you see what we've been planting and restoring all\n      around the UK, and help support our work by directly funding the trees we plant or the sites\n      we restore.\n      To get involved [contact us and ask for an access token](https://protect.earth/contact) then\n      [check out the API documentation](https://protect.earth/api).\n\n\nUsing OpenAPI Overlays you can effectively “patch” an OpenAPI description, pointing to parts of the original document with JSONPath, then adding or updating your content in. You can add as many actions to these overlays as you like, or make multiple overlays.\n\nTo work with Overlays you’ll need a tool that understands them, and that’s not all OpenAPI tools as the concept is still very new. Regardless of what API documentation tool you are using, you can use the Bump CLI to apply these overlays, and this will produce a new user-facing document.\n\nbump overlay openapi.yaml overlays.yaml &gt; openapi.public.yaml\n\n\nYou can run these commands in continuous integration, and whatever you would have done with the original you can now do with the new openapi.public.yaml (or whatever you decide to name it).\n\nWhen deploying a document to Bump.sh using the GitHub Action or the CLI, you can skip a step and point the deploy command at the overlay.\n\nname: Deploy documentation\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  deploy-doc:\n    name: Deploy API doc on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: partner-api\n          token: ${{secrets.BUMP_TOKEN}}\n          file: api/openapi.bundle.yaml\n          overlay: api/overlays.yaml\n\n\nLearn more about Overlays and using them within Bump.sh.\n\nAdding more context with Overlays\n\nEngineers will often focus very much on the “how”, but leave out some of the “why”, or really explain the “what”, so if you have an existing OpenAPI document you cannot edit directly, try adding in some of these things with overlays.\n\nTags\n\nTags are a really useful place to explain some of the concepts being used. For example an Order and an Organization might seem fairly obvious what it is to the engineers working on it, but you could add context to them.\n\nHere’s an example of an overlay you could use to expand the tag. Descriptions with a whole bunch of Markdown, and links to other resources.\n\n# overlays.yaml\noverlay: 1.0.0\ninfo:\n  title: Expand Tag Descriptions\n  version: 0.0.1\nactions:\n  - target: '$.tags[?(@.name==\"Order\")]'\n    description: Provide more information for Order tag.\n    update:\n      description: &gt;\n        The Order resource represents a single order for trees, which can be fulfilled by one or more\n        deliveries. Orders are created by the [Protect Earth team](https://protect.earth/contact) and\n        are used to track the progress of your order from creation to delivery.\n  - target: '$.tags[?(@.name==\"Organization\")]'\n    description: Provide more information for Organization tag.\n    update:\n      description: &gt;\n        The Organization resource represents a single organization, which can be a charity, business,\n        or other entity. Organizations are created by the [Protect Earth team](https://protect.earth/contact)\n        and are connected to each of your Orders.\n\n\nThese descriptions (which can be much longer and full of even more Markdown) will then show up in API Documentation, pride of place, ready to explain the concepts to the user before they get stuck into what specific endpoints are about.\n\n\n\nHere’s the tag description rendered in Bump.sh.\n\nIntroductory Topics\n\nThere are quite a few handy “vendor extensions” around which you can add more power to any tooling that knows how to respond to them. One particularly useful one is x-topics, which allows tech writers (or anyone else messing with this sort of work known as “doc ops” or “spec ops”) to expand on just the API Reference Documentation, and start introducing end-users to other guides and content.\n\nopenapi: 3.1.0\n\nx-topics:\n  - title: Getting started\n    content:\n      $ref: ./docs/getting-started.md\n\n\nIn Bump.sh this will create a new navigation entry, and insert the Markdown content from the reference guide right into the main documentation.\n\n\n\nWhether you inject x-topics with Overlays or directly into OpenAPI in the source code, the result is the same.\n\nCode Samples\n\nThere’s countless other improvements you can make to the source OpenAPI given to you by the engineering teams who have other things to be worrying about, like adding client-side code samples with x-codeSamples.\n\npaths:\n  /users:\n    get:\n      summary: Retrieve a user\n      operationId: getUserPath\n      responses: [...]\n      parameters: [...]\n      x-codeSamples:\n        - lang: ruby\n          label: Ruby library\n          source: |\n            require \"http\"\n             \n            request = HTTP\n              .basic_auth(:user =&gt; \"name\", :pass =&gt; \"password\")\n              .headers(:accept =&gt; \"application/json\")\n             \n            response = request.get(\"https://api.example.com/v1/users\")\n            if response.status.success?\n              # Work with the response.body\n            else\n              # Handle error cases\n            end\n\n\nExternal Documentation\n\nYou could add externalDocs to point them to tutorials hosted elsewhere.\n\ntags:\n  - name: Stations\n    description: Train Stations all over Europe, using a bunch of standards defined elsewhere.\n    externalDocs:\n      url: https://train-travel.example.com/docs/stations\n\n\nFilter out anything that shouldn’t be there, like beta endpoints that are not ready for public use. There’s a few ways to do this.\n\nBump.sh users can do this with the x-beta property:\n\npaths:\n  /diffs:\n    post:\n      description: Create a diff between any two given API definitions\n      x-beta: true # Beta flag at the operation level\n      requestBody:\n        description: The diff creation request object\n        content:\n          application/json:\n            schema:\n              type: object\n              x-beta: true # Beta flag at the top-level schema object\n              properties:\n                url:\n                  type: string\n                  format: uri\n                  x-beta: true # Beta flag at the object property level\n                  description: |\n                    **Required** if `definition` is not present.\n                    Current definition URL. It should be accessible through HTTP by Bump.sh servers.\n\n\nOr you can filter them out with overlays:\n\noverlay: 1.0.0\ninfo:\n  title: Remove beta flags\n  version: 0.0.1\nactions:\n  - target: \"$..[?(@['x-beta'] == true)]^\"\n    description: Remove anything beta\n    remove: true\n\n\n\n  Learn more about working with JSONPath to write powerful targets for your overlays using our guide How to work with JSONPath.\n\n\nSummary\n\nOverlays are powerful, advanced, and standardized across the toolchain, so you can rely on them to help you with any modifications you need to do.\n\nBeing able to change things however you like, then publish the changed versions off seamlessly is really handy, and will hopefully be the last time you need to do awkward JSON/YAML hacking on other peoples documents. JSONPath is a tricky thing to learn, but if you can master regex you can master JSONPath, then the world is your oyster."
        },
        {
          "id": "openapi-v3.2-extending-extensions",
          "title": "Specification Extensions in OpenAPI",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/extending/extensions/",
          "content": "Add a feedback link (x-feedbackLink)\n  Add topics to your documentation (x-topics)\n  Custom code sample examples (x-codeSamples)\n  Expose your beta features (x-beta)\n\n\nOpenAPI v3.1 has a concept of Specification Extensions which are additional properties not specified by the OpenAPI specification. These are a chance to customize and integrate tools from documentation to API gateways, all hooking into the OpenAPI document and storing information important to them in the form of extra properties that will be ignored by other tooling.\n\nAll those properties start with the x- naming convention to be identified as “eXternal” from the OpenAPI specification.\n\nAdd a feedback link (x-feedbackLink)\n\nThe x-feedbackLink object can be added directly in the info object of your OpenAPI document. Find out more in our dedicated section.\n\nAdd topics to your documentation (x-topics)\n\nThis vendor-specific property we created helps to add more context paragraphs in your generated documentation. Find out more in our dedicated section.\n\nCustom code sample examples (x-codeSamples)\n\n\n  This vendor extension is only available for OpenAPI documents for now\n\n\nWe added a custom property, not supported by OpenAPI, so you can add your own code samples in one or more programming languages to your documentation. Find out more in our dedicated section.\n\nExpose your beta features (x-beta)\n\nThis custom property allows you to identify some components of your\ndocumentation as beta. Find out more in our dedicated section."
        },
        {
          "id": "openapi-v3.1-understanding-structure-api-servers",
          "title": "Defining API Servers",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/understanding-structure/api-servers/",
          "content": "Server Variables\n\n\nThe servers section in an OpenAPI specification serves as a roadmap, detailing the various environments your API is accessible from. It’s a straightforward yet powerful way to communicate the base URLs of your API across different stages of its lifecycle, or in different environments the end-users might be interested in like a mocking server, or a sandbox for interacting with the API without real-world consequences.\n\nHere is an example of how you can define API servers in your OpenAPI specification:\n\nopenapi: 3.1.0\ninfo:\n  title: Example API\n  version: 1.0.0\n\nservers:\n  - url: http://localhost:8088/api\n    description: Development\n    x-internal: true\n\n  - url: https://staging.example.com/api\n    description: Staging\n    x-internal: true\n\n  - url: https://example.com/api\n    description: Production\n    x-internal: false\n\n\nThis example shows three API servers, for the common dev, staging, and production environments. Perhaps the local environment is on localhost and perhaps its a virtual machine on the cloud somewhere, but the idea is that same, you have all the different places an API might be.\n\n\n  The x-internal is not strictly part of the specification, but it is a popular extension. Any tools that support it will hide these servers, removing them from user facing documentation for example. This lets you can keep handy development and testing information in OpenAPI, but avoid confusing end-users with details about your internal setup.\n\n\nServer Variables\n\nServer variables offer a convenient way to modify server URLs, covering simple patterns such as environment names, geographical regions, or covering wildcards like user-generated subdomains. These variables are part of the server object, and allow for more flexible API configurations without hardcoding every possible server option.\n\nFor instance, consider an API that is deployed across multiple regions, such as the United States, Europe, and Asia. Instead of listing each server URL separately, you can use a server variable to represent the region.\n\nservers:\n  - url: \"https://{region}.api.example.com\"\n    description: Regional Production Server\n    variables:\n      region:\n        default: eu\n        description: Regions\n        enum:\n          - us\n          - eu\n          - asia\n\n\nIn this example, {region} is a server variable, and the enum restricts this to three possible values: us, eu, and asia. The default value is eu, which means if the region is not specified, tooling can know which value to use. This setup allows clients to dynamically select the appropriate regional server by substituting the {region} variable in the URL template, resulting in https://asia.api.example.com.\n\n\n  Some people try to use server variables for handling API Versions (v1, v2, v3) in a single OpenAPI document. This is a poor fit for server variables, because far more than the server URL will change between major versions. Server variables help when just the server is changing, but the other operations and components are the same."
        },
        {
          "id": "openapi-v3.1-understanding-structure-basic-structure",
          "title": "Basic Structure",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/understanding-structure/basic-structure/",
          "content": "The OpenAPI Document    \n      1. OpenAPI Object\n      2. Info Object\n      3. Servers Object\n      4. Paths Object\n      5. Components Object\n      6. Security Object\n      7. Webhooks Object\n      8. Tags Object\n    \n  \n  Example OpenAPI Document\n\n\nThe OpenAPI Specification (OAS) is an “API Description Format”, providing a standard format for describing REST APIs, making it easier to design, document, and consume them.\n\nIn this tutorial we’ll explore the structure of an OpenAPI document, focusing on the main sections and important elements, so that you can get a feel for where everything is, without having to scan through the whole OpenAPI Specification yourself.\n\nThe OpenAPI Document\n\nAn OpenAPI document is typically written in either YAML or JSON format, usually called openapi.yaml or openapi.json, but it could have any name.\n\nIt consists of several key sections that describe the API’s endpoints, requests, responses, data models, and more. Here is an outline of the main sections:\n\n\n  OpenAPI Object\n  Info Object\n  Servers Object\n  Paths Object\n  Components Object\n  Security Object\n  Webhooks Object\n  Tags Object\n\n\nLet’s look at each of these objects to see what’s going on.\n\n1. OpenAPI Object\n\nThe root of the OpenAPI document is the openapi object, which specifies the version of the OpenAPI Specification being used. For example:\n\nopenapi: 3.1.1\n\n\nThis is required, so that tooling knows which version you are working with. The 3.1 part is the important bit. The patch number (3.1.1) doesn’t really matter as those “patch” versions only add clarifications to the specification and never change meaning, but it’s helpful to know what version somebody was reading when they wrote the OpenAPI.\n\n2. Info Object\n\nThe info object holds general metadata about the API, such as the title, version, description, and contact information.\n\ninfo:\n  title: Train Travel API\n  description: |\n    API for finding and booking train trips across Europe.\n  version: 1.0.0\n  contact:\n    name: Train Support\n    url: https://example.com/support\n    email: support@example.com\n  license:\n    name: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International\n    identifier: CC-BY-NC-SA-4.0\n\n\nOnly the following fields are required:\n\n\n  title - Your API probably has a name, and if not perhaps now is a good time to think of one thats useful for public consumption.\n  version - The version of your OpenAPI document, which does not have to related to the API version, or the OpenAPI Specification version.\n\n\nUpdating the version number when you make changes is pretty common, and keeping it separate from the API version at first feels a little bit odd, but soon makes sense. After all, you can fix issues with the OpenAPI document that produce no change whatsoever in the API, and vice-versa.\n\nThese other fields are optional:\n\n\n  description - A handy place to put general information, especially introductory topics like where to find access tokens or links to various Postman/Insomnia Collections, or SDKs. This can be done using CommonMark (Markdown) to get as advanced as you want.\n  contact - Help your APIs users find the help they need instead of wandering off to use another API.\n  license - A chance to explain the license details of your API description. Note, that is very different from the license you use for your API source code, which would be licensed through source control with a LICENSE.txt or similar.\n\n\n3. Servers Object\n\nThe servers object specifies one or more server URLs where the API is hosted. Each server can have a URL and optional description.\n\nservers:\n  - url: https://api.example.com/v1\n    description: Production server\n  - url: https://staging-api.example.com/v1\n    description: Staging server\n\n\nIt’s fine to put any development and testing servers in here because you can always flag them as internal or strip them out with overlays later.\n\n4. Paths Object\n\nThe paths object is probably the most important section for your API. It lists all the available API endpoints, with each path being a key in the object. Then the object is further broken down by the specific HTTP methods supported by each endpoint. The object in each of these HTTP methods is another object which describes the “operation”, which is a term in OpenAPI to describe a specific combination of path and method.\n\npaths:\n  /bookings:\n    get:\n      operationId: get-bookings\n      summary: List existing bookings\n      tags:\n      - Bookings\n      responses:\n        '200':\n          description: A list of bookings                 \n    post:\n      operationId: create-booking\n      summary: Create a booking\n      tags:\n      - Bookings\n      requestBody:\n        required: true\n      responses:\n        '201':\n          description: Booking successful\n\n\nHere the operationId helps us spot the two different operations, and give them a unique name which can be useful for all sorts of tools. The summary gives a human readable title that will often be used in documentation tools.\n\nAny HTTP request which has a body (e.g.: POST, PUT, PATCH) can define a requestBody, and the responses are broken down by status code. This is a bit of a skeleton at the moment and ignores the media types and payloads.\n\nLearn more about paths &amp; operations.\n\n5. Components Object\n\nThe components object is where various types of reusable objects live. The main thing people use here is schemas, which some people call “data models” but that doesn’t exist anywhere in the specification, thats just a nickname.you might hear.\n\ncomponents:\n  schemas:\n    Trip:\n      type: object\n      properties:\n        id:\n          type: string\n          format: uuid\n          description: Unique identifier for the trip\n        origin:\n          type: string\n          description: The starting station of the trip\n        destination:\n          type: string\n          description: The destination station of the trip\n        departure_time:\n          type: string\n          format: date-time\n          description: The date and time when the trip departs\n        arrival_time:\n          type: string\n          format: date-time\n          description: The date and time when the trip arrives\n\n\nThe schemas defined in components.schemas let you describe common data structures used throughout your API, allowing them to be referenced via $ref whenever a schema is required: whether that is a request body, response body, parameter, or header.\n\ncomponents:\n  requestBodies:\n    TripRequest:\n      description: A request body for creating a new trip.\n      required: true\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Trip'\n\n  responses:\n    TripResponse:\n      description: A single Trip returned as a response.\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Trip'\n\n\nComponents can also define parameters which can be used across multiple endpoints:\n\ncomponents:\n  parameters:\n    pageParam:\n      in: query\n      name: page\n      required: false\n      schema:\n        type: integer\n        default: 1\n        description: The page number for pagination.\n\n\nOr common headers that can be returned across multiple endpoints:\n\ncomponents:\n  headers:\n    RateLimit:\n      description: |\n        The RateLimit header communicates quota policies. It contains a `limit` to\n        convey the expiring limit, `remaining` to convey the remaining quota units,\n        and `reset` to convey the time window reset time.\n      schema:\n        type: string\n        examples:\n          - limit=10, remaining=0, reset=10\n\n    Retry-After:\n      description: | \n        The Retry-After header indicates how long the user agent should wait before making a follow-up request. \n        The value is in seconds and can be an integer or a date in the future. \n        If the value is an integer, it indicates the number of seconds to wait. \n        If the value is a date, it indicates the time at which the user agent should make a follow-up request. \n      schema:\n        type: string\n      examples:\n        integer:\n          value: '120'\n          summary: Retry after 120 seconds\n        date:\n          value: 'Fri, 31 Dec 2021 23:59:59 GMT'\n          summary: Retry after the specified date\n\n\nOr examples, so multiple requests, responses, or parameters could share one or more examples.\n\ncomponents:\n  examples:\n    Card:\n      summary: Card Payment\n      value:\n        amount: 49.99\n        currency: gbp\n        source:\n          object: card\n          name: J. Doe\n          number: '4242424242424242'\n          cvc: 123\n          exp_month: 12\n          exp_year: 2025\n          address_line1: 123 Fake Street\n          address_line2: 4th Floor\n          address_city: London\n          address_country: gb\n          address_post_code: N12 9XX\n    Bank:\n      summary: Bank Account Payment\n      value:\n        amount: 100.5\n        currency: gbp\n        source:\n          object: bank_account\n          name: J. Doe\n          number: '00012345'\n          sort_code: '000123'\n          account_type: individual\n          bank_name: Starling Bank\n          country: gb\n\n\nOr securitySchemes which will be called with the security keyword. OpenAPI supports several authentication types, but here are a few examples:\n\ncomponents:\n  securitySchemes:\n    ApiKeyHeader:\n      type: apiKey\n      in: header\n      name: X-API-Key\n\n    BearerToken:\n      type: http\n      scheme: bearer\n    \n    JWT:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\n   \n    OAuth2ReadWrite:\n      type: oauth2\n      flows:\n        authorizationCode:\n          scopes:\n            read: Grants read access\n            write: Grants write access\n          authorizationUrl: https://example.com/oauth/authorize\n          tokenUrl: https://example.com/oauth/token\n          refreshUrl: https://example.com/oauth/refresh\n\n\nThis is just a few of the many types of security schemes that can be defined, but defining them alone doesn’t do anything. They need to be referenced by the security object.\n\n6. Security Object\n\nThe top-level security list specifies the security schemes that apply globally to the API, so if an entire API uses an API key or OAuth2 you might have:\n\nsecurity:\n  - apiKey: []\n  - oauth2:\n    - read\n    - write\n\n\nYou can get into path specific overrides and various complex “and” situations with more advanced security functionality.\n\n7. Webhooks Object\n\nwebhooks:\n  newBooking:\n    post:\n      operationId: new-booking\n      summary: New Booking\n      description: |\n        Subscribe to new bookings being created, to update integrations for your users.  Related data is available via the links provided in the request.\n      tags:\n        - Bookings\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/Booking'\n      responses:\n        '200':\n          description: Return a 200 status to indicate that the data was received successfully.\n\n\n8. Tags Object\n\nYou may have spotted the tags keyword in the paths and webhooks, and those are referencing tags defined in the top-level tags object. The tag name is used to group related operations together. Each tag has a name and an optional description.\n\ntags:\n  - name: Bookings\n    description: | \n      Create and manage bookings for train trips, including passenger details\n      and optional extras.\n  - name: Payments\n    description: |\n      Pay for bookings using a card or bank account, and view payment\n      status and history.\n\n      &gt; warn\n      &gt; Bookings usually expire within 1 hour so you'll need to make your payment\n      &gt; before the expiry date \n\n\nThe name is often displayed to users in human-readable documentation so its best to make it “Title Case”, and the description can be quite long, think paragraphs not sentences, explaining what this concept is to the user as that will also show up in most documentation tools.\n\nExample OpenAPI Document\n\nPutting it all together, here is a simple example of an OpenAPI document:\n\nopenapi: 3.1.1\ninfo:\n  title: Sample API\n  description: A sample API to illustrate OpenAPI concepts.\n  version: 1.0.0\n  contact:\n    name: API Support\n    url: http://www.example.com/support\n    email: support@example.com\nservers:\n  - url: https://api.example.com/v1\n    description: Production\npaths:\n  /users:\n    get:\n      summary: List all users\n      responses:\n        '200':\n          description: A list of users\n          content:\n            application/json:\n              schema:\n                type: array\n                items:\n                  $ref: '#/components/schemas/User'\n    post:\n      summary: Create a new user\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/User'\n      responses:\n        '201':\n          description: User created\ncomponents:\n  schemas:\n    User:\n      type: object\n      properties:\n        id:\n          type: integer\n          format: int64\n        username:\n          type: string\n        email:\n          type: string\n          format: email\nsecurity:\n  - api_key: []\ntags:\n  - name: users\n    description: Operations related to users\n\n\nFor a more advanced example, take a look at the Train Travel API, the modern OpenAPI example from Bump.sh."
        },
        {
          "id": "openapi-v3.1-understanding-structure-components",
          "title": "OpenAPI Components",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/understanding-structure/components/",
          "content": "Using $ref with Components\n  Create “Design Libraries” of Shared Components\n\n\nThe Components object in OpenAPI allows you to create reusable bits of OpenAPI that can then be pieced together like Lego blocks to build a better API description. This keeps things nice and tidy, and you can even spread them across multiple documents to share components between multiple APIs, or at least just keep your file sizes down.\n\ncomponents:\n  schemas:\n    User:\n      type: object\n      properties:\n        id:\n          type: integer\n        name:\n          type: string\n        email:\n          type: string\n          format: email\n  parameters:\n    userId:\n      name: id\n      in: path\n      description: ID of the user\n      required: true\n      schema:\n        type: integer\n  responses:\n    NotFound:\n      description: User not found\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n  requestBodies:\n    User:\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/User'\n  securitySchemes:\n    bearerAuth:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\n\n\nThe full list of objects which can be defined in components is:\n\n\n  callbacks - Define callback objects that send outgoing requests.\n  examples - Define reusable examples for whole media types.\n  headers - Define reusable HTTP header objects to be included in responses.\n  links - Define reusable links between operations.\n  parameters - Define reusable parameters that can be used in requests.\n  pathItems - Define reusable path items which can go into paths and webhooks.\n  requestBodies - Define reusable request body objects for operations.\n  responses - Define reusable response objects for operations.\n  schemas - Define reusable schemas for media types and any other object which accepts schemas.\n  securitySchemes - Define reusable security schemes for API authentication and authorization.\n\n\nUsing $ref with Components\n\nOnce components have been defined they can be referenced with $ref. This is mostly the same definition of $ref in JSON Schema so it can help to learn how that works, but there are a few caveats to keep in mind.\n\nThe OpenAPI Documentation from the OpenAPI Initiative includes a brilliant example of an API for playing the classic board game Tic Tac Toe, and it demonstrates $ref nicely.\n\nThis has several parts that are used several times, so instead of copy-pasting everything they’ve defined reusable components for both schemas and parameters.\n\npaths:\n  # Whole board operations\n  /board:\n    get:\n      summary: Get the whole board\n      description: Retrieves the current state of the board and the winner.\n      tags:\n        - Gameplay\n      operationId: get-board\n      responses:\n        \"200\":\n          description: \"OK\"\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/status\"\n  # Single square operations\n  /board/{row}/{column}:\n    parameters:\n      - $ref: \"#/components/parameters/rowParam\"\n      - $ref: \"#/components/parameters/columnParam\"\n    get:\n      # ... Hidden for readability...\n    put:\n      # ... Hidden for readability...\n\ncomponents:\n  parameters:\n    rowParam:\n      description: Board row (vertical coordinate)\n      name: row\n      in: path\n      required: true\n      schema:\n        $ref: \"#/components/schemas/coordinate\"\n    columnParam:\n      description: Board column (horizontal coordinate)\n      name: column\n      in: path\n      required: true\n      schema:\n        $ref: \"#/components/schemas/coordinate\"\n  schemas:\n    errorMessage:\n      type: string\n      maxLength: 256\n      description: A text message describing an error\n    coordinate:\n      type: integer\n      minimum: 1\n      maximum: 3\n      example: 1\n    mark:\n      type: string\n      enum: [\".\", \"X\", \"O\"]\n      description: Possible values for a board square. `.` means empty square.\n      example: \".\"\n    board:\n      type: array\n      maxItems: 3\n      minItems: 3\n      items:\n        type: array\n        maxItems: 3\n        minItems: 3\n        items:\n          $ref: \"#/components/schemas/mark\"\n    winner:\n      type: string\n      enum: [\".\", \"X\", \"O\"]\n      description: Winner of the game. `.` means nobody has won yet.\n      example: \".\"\n    status:\n      type: object\n      properties:\n        winner:\n          $ref: \"#/components/schemas/winner\"\n        board:\n          $ref: \"#/components/schemas/board\"\n\n\nHopefully this gives an idea of how $ref can be used, and if you’d like to learn more check out our advanced guide: Splitting Documents with $ref.\n\nCreate “Design Libraries” of Shared Components\n\nAn OpenAPI document does not need to contain paths or webhooks, it could be just a components object with nothing else.\n\nOne of more of these “components only” documents could then be shared around forming a rudimentary “design library”, helping teams reuse data models and various other bits across multiple APIs, multiple departments, or even externally to your organization.\n\nThere are various proprietary tools out there to help with this, but the concept can be achieved by just sharing these openapi-components.yaml or similar on your network drive, Git repository, intranet, or public website."
        },
        {
          "id": "openapi-v3.1-understanding-structure-parameter-serialization",
          "title": "Parameter Serialization",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/understanding-structure/parameter-serialization/",
          "content": "Explode\n  Style\n  Path Parameters    \n      Simple\n      Label\n      Matrix\n    \n  \n  Query Parameters    \n      Form\n      Space Delimited\n      Pipe Delimited\n      Deep Object\n    \n  \n  Header Parameters\n  Cookie Parameters\n  Examples and Recommendations    \n      General Guide Lines        \n          Location\n          Style\n        \n      \n      Optional Boolean\n      Required String\n      List of Strings\n      AnyOf Object or String\n    \n  \n\n\nParameters not only define what inputs your API accepts, they also define the format your API expects to receive them in, i.e. how you would like it serialized.\n\nThere are two keywords concerning serialization:\n\nExplode\n\nexplode defines whether parameters should be broken into logical components.\n\nIt takes a boolean value:\n\n  If true; a parameter with multiple values will be serialized as if each of its values were separate parameters.\n    \n      What separates each parameter is determined by the style.\n    \n  \n  If false; a parameter is a single parameter, regardless of how many values it has.\n\n\nIn practice, this means only parameters of type:array or type:object are affected by explode.\n\n  For an array, each value becomes its own parameter.\n  For an object, each key-value pair is concatenated into its own parameter as “key=value”.\n    \n      For any style other than form, if the value is an empty string, then it drops the equals and becomes “key”\n    \n  \n\n\nFor a more verbose description of explode, refer to RFC6750’s Variable Expansion.\n\nIts default value depends on the style of serialization:\n\n  explode:true is the default for style:form\n  explode:false for anything else.\n\n\nStyle\n\nstyle defines how your API expects the parameter to be serialized.\n\nIt takes a string value: The options defined depend on the location your parameter is in:\n\n  in:path defaults to simple but can also be label or matrix.\n  in:query defaults to form but can also be spaceDelimited, pipeDelimited or deepObject.\n  in:header defaults to simple.\n  in:cookie defaults to form.\n\n\nEach style will be explained in more depth per location; examples will make use of the following two parameters.\n\n“pets” which depending on its type has one of the following values:\n\nbool   -&gt; true\nint    -&gt; 2\nstring -&gt; \"dog\"\narray  -&gt; [\"cat\",\"dog\"]\nobject -&gt; {\"age\":2,\"type\":\"dog\"}\n\n\n“hats” which depending on its type has one of the following values:\n\nbool   -&gt; false\nint    -&gt; 1,\nstring -&gt; \"fedora\"\narray  -&gt; [\"fedora\"]\nobject -&gt; {\"type\":\"fedora\"}\n\n\nPath Parameters\n\nFor parameters in:path there are three defined values for style:\n\n\n  simple: defined by RFC6750’s Simple String Expansion.\n  label: defined by RFC6750’s Label Expansion with Dot-Prefix.\n  matrix: defined by RFC6750’s Path-Style Parameter Expansion.\n\n\nThe defaults in:path are:\n\n  style:simple\n  explode:false\n\n\nEvery style in:path follows RFC6750 so the effects of explode are well-defined by RFC6570’s Variable Expansion.\n\nSimple\n\nstyle:simple with its default of explode:false, would serialize your parameters like this:\n\n\n  \n    \n      empty\n      bool\n      int\n      string\n      array\n      object\n    \n  \n  \n    \n       \n      true\n      2\n      dog\n      cat,dog\n      age,2,type,dog\n    \n  \n\n\n\n  Single values are unchanged.\n  An array with multiple values is concatenated into a comma-delimited list.\n  An objecthas its key-value pairs concatenated into comma-delimited pairs, then each pair is concatenated into a comma-delimited list.\n\n\nIf you set explode:true, then the seperator used is also a comma: “,”:\n\n\n  \n    \n      empty\n      bool\n      int\n      string\n      array\n      object\n    \n  \n  \n    \n       \n      true\n      2\n      dog\n      cat,dog\n      age=2,type=dog\n    \n  \n\n\n\n  Single values remain unchanged.\n  Surprisingly, an array with multiple values seems unchanged. Though it treated each value as a separate parameter, it still had to separate them with a comma. So it still ends up as a comma-delimited list.\n  To understand what happened for an object looking back at the rules on explode we see it concatenates key-value pairs into their own parameters as “key=value”. Then it has to separate each parameter with a comma.\n\n\nLabel\n\nstyle:label with its default of explode:false, would  serialize your parameters like this:\n\n\n  \n    \n      empty\n      bool\n      int\n      string\n      array\n      object\n    \n  \n  \n    \n       \n      .true\n      .2\n      .dog\n      .cat,dog\n      .age,2,type,dog\n    \n  \n\n\nEverything is the same as style:simple except all parameters were prefixed with “.”.\n\nIf you set explode:true, then the seperator used is a period: “.”:\n\n\n  \n    \n      empty\n      bool\n      int\n      string\n      array\n      object\n    \n  \n  \n    \n       \n      .true\n      .2\n      .dog\n      .cat.dog\n      .age=2.type=dog\n    \n  \n\n\n\n  Single values remain unchanged.\n  An array becomes a period-delimited list.\n  An object concatenates key-value pairs into their own parameters as “key=value”. Then it separates each parameter with a period.\n\n\nMatrix\n\nstyle:matrix with its default of explode:false, would  serialize your parameters like this:\n\n\n  \n    \n      empty\n      bool\n      int\n      string\n      array\n      object\n    \n  \n  \n    \n       \n      ;pets=true\n      ;pets=2\n      ;pets=dog\n      ;pets=cat,dog\n      ;pets=age,2,type,dog\n    \n  \n\n\nEverything is the same as style:simple except all parameters were prefixed with a semicolon: “;pets=” where “pets” is the parameter’s name.\n\nIf you set explode:true, then the seperator used is a semicolon: “;”.\n\n\n  \n    \n      empty\n      bool\n      int\n      string\n      array\n      object\n    \n  \n  \n    \n       \n      ;pets=true\n      ;pets=2\n      ;pets=dog\n      ;pets=cat;pets=dog\n      ;age=2;type=dog\n    \n  \n\n\n\n  Single values remain unchanged.\n  An array has its values treated as separate parameters. Because they’re now treated separately, every value is prefixed with “;pets=”\n  An object is the exception, it does not get prefixed with “pets=”, but it still has to separated by a semicolon: “;”.\n\n\nQuery Parameters\n\nFor parameters in:query there are four defined values for style.\n\n\n  form: It is defined by RFC6750’s Form-Style Query Expansion, if there are multiple\n  spaceDelimited: An addition by popular demand.\n  pipeDelimited: An addition by popular demand.\n  deepObject: An addition by popular demand.\n\n\nThe defaults in:query are:\n\n  style:form\n  explode:true\n\n\nOnly style:form follows RFC6750 so the effects of explode are only well-defined by RFC6570’s Variable Expansion for style:form.\n\nAn informal, general rule of thumb is:\n\n  Query strings start with a question-mark, this is how you separate the first query parameter from the rest of the URI.\n  Subsequent parameters in:query are separated by an ampersand “&amp;”.\n\n\nJust be aware that spaceDelimited, pipeDelimited and deepObject are not defined by RFC6750.\nThere are caveats to their usage, if you intend to use them, make sure you read their sections carefully.\n\nForm\n\nWith style:form, if you set explode:false, would serialize your parameters like this:\n\n\n  \n    \n      empty\n      bool\n      int\n      string\n      array\n      object\n    \n  \n  \n    \n       \n      ?pets=true\n      ?pets=2\n      ?pets=dog\n      ?pets=cat,dog\n      ?pets=age,2,type,dog\n    \n    \n       \n      ?pets=true&amp;hats=false\n      ?pets=2&amp;hats=1\n      ?pets=dog&amp;hats=fedora\n      ?pets=cat,dog&amp;hats=fedora\n      ?pets=age,2,type,dog&amp;hats=type,fedora\n    \n  \n\n\nYou’ll notice this looks almost identical to style:matrix. \nThere’s only one difference to be\n\n  If it’s the first parameter, the separator from the rest of the URI by a question-mark like above ?pets=true\n  If it’s the second parameter, the separator is an ampersand, you might have a query string like this ?hats=false&amp;pets=true\n\n\nIf you stick with the default of explode:true, then the seperator used is also a comma: “,”:\n\n\n  \n    \n      empty\n      bool\n      int\n      string\n      array\n      object\n    \n  \n  \n    \n       \n      ?pets=true\n      ?pets=2\n      ?pets=dog\n      ?pets=cat&amp;pets=dog\n      ?age=2&amp;type=dog\n    \n    \n       \n      ?pets=true&amp;hats=false\n      ?pets=2&amp;hats=1\n      ?pets=dog&amp;hats=fedora\n      ?pets=cat&amp;pets=dog&amp;hats=fedora\n      ?age=2&amp;type=dog&amp;type=fedora\n    \n  \n\n\nNotice one example is highlighted in red. The OpenAPI Specification states that A Unique Parameter is a combination of name and (in).\nBoth “pets” and “hats” would be considered unique parameters, but they both have the property “type”. When explode is true their properties are serialized as if they were separate parameters. It is as if we have two different parameters both with name:type, in:query, they are no longer unique and one cannot be unambiguously distinguished from the other.\n\nThis conflict is entirely avoided if you explicitly set explode:false on parameters of type:object, but if that’s not an option, remain vigil for possible conflicts.\n\nSpace Delimited\n\nstyle:spaceDelimited with its default of explode:false, would  serialize your parameters like this:\n\n\n  \n    \n      array\n      object\n    \n  \n  \n    \n      ?pets=cat%20dog\n      ?pets=age%202%20type%20dog\n    \n    \n      ?pets=cat%20dog&amp;hats=fedora\n      ?pets=age%202%20type%20dog&amp;hats=type%20fedora\n    \n  \n\n\nIt’s basically identical to style:form with explode:false. The difference being, the separator used is not a comma, but a percent-encoded space “%20”.\n\nYou’ll notice there are no examples for any type that would be a single value. This is because its behaviour is undefined for single values. One could assume it would be identical to style:form, but if your parameter is going to be a single value, there is no need to explicitly define it as spaceDelimited.\n\nstyle:spaceDelimited is not defined by RFC6750 and there is no defined behaviour for explode:true. You could assume it would be identical to the well-defined in:query default of style:form with explode:true. That said, if you’re making that assumption, you’re better off leaving it on the well-defined default.\n\nPipe Delimited\n\nstyle:pipeDelimited with its default of explode:false, would  serialize your parameters like this:\n\n\n  \n    \n      array\n      object\n    \n  \n  \n    \n      ?pets=cat%7Cdog\n      ?pets=age%7C2%7Ctype%7Cdog\n    \n    \n      ?pets=cat%7Cdog&amp;hats=fedora\n      ?pets=age%7C2%7Ctype%7Cdog&amp;hats=type%7Cfedora\n    \n  \n\n\nIt’s basically identical to style:form with explode:false. The difference being, the separator used is not a comma, but a percent-encoded pipe “%7C”.\n\nYou may be able to use a normal pipe “|” but it is not in the list of RFC3986’s Unreserved Characters. As such, it may work in some environments, and not in others.\nIf you still choose to use non-percent-encoded pipes, it would look like this:\n\n\n  \n    \n      array\n      object\n    \n  \n  \n    \n      ?pets=cat|dog\n      ?pets=age|2|type|dog\n    \n    \n      ?pets=cat|dog&amp;hats=fedora\n      ?pets=age|2|type|dog&amp;hats=type|fedora\n    \n  \n\n\nYou’ll notice there are no examples for any type that would be a single value. This is because its behaviour is undefined for single values. One could assume it would be identical to style:form, but if your parameter is going to be a single value, there is no need to explicitly define it as spaceDelimited.\n\nstyle:pipeDelimited is not defined by RFC6750 and there is no defined behaviour for explode:true. You could assume it would be identical to the well-defined in:query default of style:form with explode:true. That said, if you’re making that assumption, you’re better off leaving it on the well-defined default.\n\nDeep Object\n\nstyle:deepObject is undefined for its default of explode:false. You must explicitly specify explode:true for any defined behaviour.\n\nYou may be able to use a normal square brackets “[” and “]” but they are in the list of RFC3986’s Reserved Characters. As such, it may not work in some environments.\n\n\n  \n    \n      object\n    \n  \n  \n    \n      ?pets[age]=2&amp;pets[type]=dog\n    \n    \n      ?pets[age]=2&amp;pets[type]=dog&amp;hats[type]=fedora\n    \n  \n\n\nFor maximum interoperability it is safer to have them percent-encoded:\n\n  “%5B” for “[”\n  “%5D” for “]”.\n\n\n\n  \n    \n      object\n    \n  \n  \n    \n      ?pets%5Bage%5D=2&amp;pets%5Btype%5D=dog\n    \n    \n      ?pets%5Bage%5D=2&amp;pets%5Btype%5D=dog&amp;hats%5Btype%5D=fedora\n    \n  \n\n\nUnsurprisingly, it only has defined behaviour for an object. This style is quite different from any other, even with explode:true the name, key and value are all specified. This makes it useful for avoiding the potential name conflicts objects could cause with style:form, explode:true.\n\nJust bear in mind the name is misleading, despite being called a deepObject, there is no defined behaviour for nested arrays or objects. This is the same for every style in:query.\n\nHeader Parameters\n\nFor parameters in:header there is only one defined value for style: simple.\n\nNaturally, the default value is style:simple, with explode:false.\n\nIt is the same definition as it would be in:path except there is a major caveat to be aware of:\n\n  Headers do not require any percent encoding in the same way a URI string would, so it cannot follow the same definitions laid out by RFC6750.\n\n\nFor this reason it is not recommended to rely on style, explode and schema.\n\nFor parameters in:header it is recommended to make use of the parameter’s content field instead of schema. Then use a media type such as text/plain and require the application to assemble the correct string. This will be the recommended approach as of OpenAPI Version 3.1.1, with more detail available in Appendix D: Serializing Headers and Cookies.\n\nCookie Parameters\n\nFor parameters in:cookie there is only one defined value for style: form.\n\nNaturally, the default value is style:form, with explode:true.\n\nIt is the same definition as it would be in:query except there are several major caveats to be aware of:\n\n  Cookies do not require any percent encoding in the same way a URI string would, so it cannot follow the same definitions laid out by RFC6750.\n  The first parameter is not prefixed with a question-mark “?” like it would in:query.\n  Any subsequent parameters are not separated by an ampersand “&amp;” like they would in:query.\n    \n      Subsequent parameters in:cookie are separated by a semicolon followed by a space “; “.\n    \n  \n\n\nAs such style:form in:cookie is somewhat confusing, and less accurate the more parameters you have to serialize. For this reason it is not recommended to rely on style, explode and schema.\n\nFor parameters in:cookie it is recommended to make use of the parameter’s content field instead of schema. Then use a media type such as text/plain and require the application to assemble the correct string. This will be the recommended approach as of OpenAPI Version 3.1.1, with more detail available in Appendix D: Serializing Headers and Cookies.\n\nExamples and Recommendations\n\nGeneral Guide Lines\n\nLocation\n\nIf a parameter is needed across many paths, or contains sensitive information; it may be sensible to include in:header or in:cookie.\n\n  If the parameter needs to persist across sessions, keep it in:cookie.\n\n\nIf a parameter is only needed in specific paths, it may be sensible include in:path or in:query.\n\nIt is easier to provide parameters in a URL. Requiring headers, cookies or a requestBody generally make requests more difficult.\nKeep it simple; if it’s sensible to include a parameter in:path or in:query, do so.\n\nStyle\n\nFor parameters in:path or in:query; the defaults exist for a reason, they’re well-defined, versatile and simple.\n\nFor parameters in:header or in:cookie; the defaults work to an extent, but the variations on their syntax are beyond the scope of the OpenAPI Specification and what can be described through style. The recommended approach is to forgo style and schema in favour of using content with a media type such as text/plain.\n\nOptional Boolean\n\nLooking at the Train Travel API, we can make a GET request to find available trips, based on our criteria:\n\n/trips:\n  get:\n  ...\n  parameters:\n    ...\n    - name: dogs\n    in: query\n    description: Only return trips where dogs are known to be allowed\n    required: false\n    schema:\n      type: boolean\n      default: false\n\n\nThe parameter is simple, it could be formatted anywhere without issue.\n\nIt doesn’t need to persist between sessions, so it doesn’t need to be in:cookie.\nIt’s specific to this path, so there’s not much benefit in sticking it in:header.\nIt’s optional, so it cannot be in:path.\n\nNo style has been mentioned, nor explode. But the parameter is in:query so we know the default is style:form and explode:true. We would expect a URLs like this:\n\nUser without a dog: /trips\nUser with a dog: /trips?dogs=true\n\nWe could set explode:false but Explode has no effect on parameters that are not arrays or objects. This would be extra documentation with no gain, leaving it as the default keeps your specification concise.\n\nWe could not use any other style available to Query Parameters as only style:form can be used with parameters that are not arrays or objects.\n\nRequired String\n\nLooking at the Train Travel API once more, we can get the details of specific bookings:\n\n/bookings/{bookingId}:\n  parameters:\n    - name: bookingId\n      in: path\n      required: true\n      description: The ID of the booking to retrieve.\n      schema:\n        type: string\n        format: uuid\n        example: 1725ff48-ab45-4bb5-9d02-88745177dedb\n  get:\n    ...\n\n\nAgain the parameter is simple, it could be formatted anywhere without issue.\n\nIt doesn’t need to persist between sessions, so it doesn’t need to be in:cookie.\nIt’s specific to this path, so there’s not much benefit in sticking it in:header.\nIt’s required, so it could be in:path or in:query.\n\nBecause parameters in:path are always required:true, it is the most intuitive place to stick a required parameter.\n\nBy default this be style:simple and explode:false, looking like this: /bookings/1725ff48-ab45-4bb5-9d02-88745177dedb\n\nIt could have a different style like so:\n\n\n  style:label : bookings/.1725ff48-ab45-4bb5-9d02-88745177dedb\n  style:matrix : bookings/;bookingId=1725ff48-ab45-4bb5-9d02-88745177dedb\n\n\nList of Strings\n\nWhat if we could filter trips that stop at a specified list of stations?\n\nIt’s specific to this path, so we should keep it in the URL for simplicity. That means in:path or in:query.\nNot every user knows the station they want, they may simply be looking for the closest stop to their actual destination. This parameter should be optional, so it cannot be in:path.\n\nWe could put it in:path and it would look like so:\n\n/trips:\n  get:\n  ...\n    parameters:\n      ...\n      - name: stations\n        in: query\n        description: Only return trips that stop at these stations\n        required: false,\n        schema:\n          type: array\n          items:\n            type: string\n\n\nNow our URL will look like this:\n\n\n  Users with specific station in mind: /trips?stations=gatwick&amp;stations=london\n  Users with only one station in mind: /trips?stations=london\n\n\nAnyOf Object or String\n\n/trips:\n  get:\n  ...\n    parameters:\n      ...\n      - name: station\n        description: Only return trips that stop at your preferred station, if none, use to fallback if provided.\n        in: query\n      style: deepObject\n      explode: true\n      required: false\n      schema:\n        anyOf: \n          - type: object\n            required:\n              - preferred\n            properties:\n              preferred:\n                type: string\n              fallback:\n                type: string\n          - type: string\n\n\nHere I’ve stated that my schema can be anyOf the following: an object or a string, in style:deepObject. You may have spotted the problem already:\n\n\n  If our user specifies an object, this works as expected: /trips?station[preferred]=gatwick&amp;station[fallback]=london.\n  What if our user specifies a string? It’s undefined, deepObject only has defined behaviour for objects.\n\n\nYou cannot apply style on a per-schema basis. Your style needs to work for all possible variations of your parameter.\nIf you intend to use anyOf, allOf or oneOf make doubly sure your choice of style works for every option.\nAs always, the best option is to minimise your use of complex parameters, keep it simple."
        },
        {
          "id": "openapi-v3.1-understanding-structure-parameters",
          "title": "Defining Parameters",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/understanding-structure/parameters/",
          "content": "Parameter Types    \n      Path Parameters\n      Query Parameters\n      Header Parameters\n      Cookie Parameters\n    \n  \n  Defining Parameters for Multiple Operations\n  Defining Shared Parameters in Components\n\n\nParameters in OpenAPI v3.1 are a fundamental part of creating an API specification, allowing you to define the inputs your API can accept.\n\nParameters fall into one of a few types:\n\n\n  Path Parameters: Variables within the path, e.g., /bookings/{bookingId}.\n  Query Parameters: Appended to the URL, e.g., /bookings?date=2024-05-01.\n  Header Parameters: Included in the request header, e.g., Acme-Custom-Header: Value.\n  Cookie Parameters: Passed in the request cookies.\n\n\n\n  In previous versions of OpenAPI the entire request body and form data would all be sent as parameters, but since OpenAPI v3.0 this has been moved to the content object. Learn more in HTTP Requests.\n\n\nEach parameter in OpenAPI is defined with specific attributes such as name, in (location), required, description, and schema (for defining data types and validation rules). Defining parameters with these keywords allows documentation to show example how HTTP requests should be constructed making life easier for the client, but also make sure machines know what to do with it, making SDKs and server-side validation a whole lot more powerful.\n\nParameter Types\n\nPath Parameters\n\nThe first type of parameter to get the hang of is path parameters.\n\n  /bookings/{bookingId}:\n    get:\n      parameters:\n        - name: bookingId\n          in: path\n          required: true\n          description: The ID of the booking to retrieve.\n          schema:\n            type: string\n            format: uuid\n          example: 1725ff48-ab45-4bb5-9d02-88745177dedb\n\n\nHere is one required path parameter, bookingId, with its name matching {bookingId}. The schema can contain anything you’d expect to find in schema, from data types to other validations.\n\n\n  Path parameters have to be marked as required: true because they’re in the path, and if its missing it would break especially if the variable was between two other segments, e.g: /bookings/{bookingId}/payment would become /bookings//payment if the value was empty and that’s going to be confusing.\n\n\nOpenAPI v3.1 is very particular about allowed characters:\n\n\n  The value for these path parameters MUST NOT contain any unescaped “generic syntax” characters described by RFC3986: forward slashes (/), question marks (?), or hashes (#).\n\n\nThis means it’s best to just use normal A-Z and 0-9 characters in the names for your path parameters.\n\nQuery Parameters\n\n  /trips:\n    get:\n      parameters:\n        - name: origin\n          in: query\n          description: The ID of the origin station\n          required: true\n          schema:\n            type: string\n            format: uuid\n          example: efdbb9d1-02c2-4bc3-afb7-6788d8782b1e\n        - name: destination\n          in: query\n          description: The ID of the destination station\n          required: true\n          schema:\n            type: string\n            format: uuid\n          example: b2e783e1-c824-4d63-b37a-d8d698862f1d\n        - name: date\n          in: query\n          description: The date and time of the trip in ISO 8601 format in origin station's timezone.\n          schema:\n            type: string\n            format: date-time\n          example: '2024-02-01T09:00:00Z'\n\n\nIn this example origin, destination, and date are query parameters. The first two are defined as required, because it’s important to know where you’re going from and to when buying a ticket, but the date is optional at this point because a customer might be looking for the cheapest day.\n\nQuery parameters are appended to the URL when a client actually makes the request, e.g., /trips?origin=efdbb9d1-02c2-4bc3-afb7-6788d8782b1e&amp;destination=destination&amp;date=2024-05-01T10:00:00.\n\nHeader Parameters\n\nHeader parameters are sent in the HTTP request as a HTTP header. HTTP headers are often are often used for passing authorization tokens, specifying content types being sent, requesting the types being received, and directing the behavior of cache mechanisms. Some of this is already covered by other OpenAPI functionality so you don’t need to manually re-define Content-Type or Accept, but anything else will need to be defined.\n\nFor example, if you’d like to let API users know they can ask for fresh (uncached) data on a certain endpoint, you can advertise the API respects the If-Modified-Since header like this:\n\npaths:\n  /trips:\n    get:\n      summary: Get train trips\n      parameters:\n        - in: header\n          name: If-Modified-Since\n          schema:\n            type: string\n            format: date-time\n          required: false\n          description: &gt;\n            Allows the client to request the resource only if it has been modified after the specified date and time.\n      responses:\n        '200':\n          content:\n            application/json:\n              schema:\n                type: array\n                items:\n                  $ref: '#/components/schemas/Trips'\n        '304':\n          description: The data has not been modified since the date and time specified in the `If-Modified-Since` header.\n\n\nTry to clearly explain not just what the header does, but in what scenarios a client might want to use it, and focus on how it helps them.\n\nCookie Parameters\n\nCookie parameters are sent in the HTTP request through the Cookies functionality available in all web browsers and some HTTP clients.\n\nCookie parameters can be any primitive values, arrays and objects. Arrays and objects are serialized using the form style. For more information, see Parameter Serialization.\n\nThe first thought might be to use cookie for authentication, but for that you would be better off using API keys. Cookie parameters are reserved for other things, like tracking and analytics, locale preferences, or other session related information which does not fit into the HTTP specification with dedicated headers.\n\npaths:\n  /analytics/visit:\n    get:\n      summary: Track user visit\n      description: Records user visit for analytics purposes.\n      parameters:\n        - name: UserId\n          in: cookie\n          required: false\n          description: Unique user identifier\n          schema:\n            type: string\n            example: \"abc123\"\n        - name: VisitCount\n          in: cookie\n          required: false\n          description: Number of visits by the user\n          schema:\n            type: integer\n            example: 5\n\n\nDefining Parameters for Multiple Operations\n\nAll these examples show parameters being defined at the operation level, but they can also be defined at the path level to avoid repetition. This is especially useful for path parameters, but works for all types of parameters.\n\n  /bookings/{bookingId}:\n    parameters:\n      - name: bookingId\n        in: path\n        required: true\n        description: The ID of the booking to retrieve.\n        schema:\n          type: string\n          format: uuid\n        example: 1725ff48-ab45-4bb5-9d02-88745177dedb\n    get:\n      ...\n    delete:\n      ...\n\n\nBy defining the bookingId parameter at the path level, it will be automatically applied to all operations under the /bookings/{bookingId} path.\n\nDefining Shared Parameters in Components\n\nAlternatively, you can define shared parameters in the components section of your OpenAPI specification. This allows you to reuse the parameters across different paths and operations. Here’s an example:\n\ncomponents:\n  parameters:\n    bookingId:\n      name: bookingId\n      in: path\n      required: true\n      description: The ID of the booking to retrieve.\n      schema:\n        type: string\n        format: uuid\n      example: 1725ff48-ab45-4bb5-9d02-88745177dedb\n\n\nTo use the shared parameter, you can reference it in your path or operation like this:\n\n  /bookings/{bookingId}:\n    get:\n      parameters:\n        - $ref: '#/components/parameters/bookingId'\n    delete:\n      parameters:\n        - $ref: '#/components/parameters/bookingId'\n\n\nThis way, you can maintain consistency and avoid duplicating parameter definitions across your API description."
        },
        {
          "id": "openapi-v3.1-understanding-structure-paths-operations",
          "title": "Paths and Operations",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/understanding-structure/paths-operations/",
          "content": "Defining Paths\n\n\nOpenAPI has the concept of “paths” and “operations”, which is two parts of what people would think of as an “endpoint”. The path covers the URL and the operation covers the rest of it.\n\nHere are a list of paths in the Train Travel API example.\n\npaths:\n  /stations:\n  /trips:\n  /bookings:\n  /bookings/{bookingId}:\n  /bookings/{bookingId}/payment:\n\n\nThe path defines the relative path of the API endpoint from wherever the server URL ends, which in this example is https://api.example.com, which together describe full URLs like this:\n\n\n  https://api.example.com/stations\n  https://api.example.com/trips\n  https://api.example.com/bookings\n  https://api.example.com/bookings/{bookingId}\n  https://api.example.com/bookings/{bookingId}/payment\n\n\nPaths can store variables, a little bit like the concept of server variables, using curly braces {} as a placeholder for a parameter which will be defined within the operation.\n\nDefining Paths\n\nEach path can then define one or more operations, using HTTP methods like get, post, put, patch, or delete as a key and the operation as an object inside that.\n\n  /bookings:\n    get:\n      operationId: get-bookings\n      summary: List existing bookings\n      description: Returns a list of all trip bookings by the authenticated user.\n      responses:\n        '200':\n          description: A list of bookings\n          content:\n            application/json:\n              schema:\n                type: array\n                items:\n                  $ref: '#/components/schemas/Booking'\n\n    post:\n      operationId: create-booking\n      summary: Create a booking\n      description: A booking is a temporary hold on a trip. It is not confirmed until the payment is processed.\n      security:\n        - OAuth2:\n            - write\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/Booking'\n      responses:\n        '201':\n          description: Booking successful\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Booking'\n\n\nEach operation should have an operationId which is really useful for all sorts of automated tooling, and a summary which is more human-readable and helps the operation show up nicely in documentation tools.\n\nThe description can then be as long and complex as you want, using CommonMark (standardized Markdown) and multi-line YAML syntax to place all the context which cannot be picked up from just looking at variable names.\n\nAny HTTP request which has a body (e.g.: POST, PUT, PATCH) can define a requestBody, which can be marked as required or not. Each request can have multiple content types, supporting JSON, XML, CSV, images, whatever you need to define.\n\nA common example would be supporting XML and JSON, but is really helpful for APIs which support image uploads being supported simultaneously via a direct Content-Type: image/* upload, whilst also supporting JSON sending the URL (e.g.: \"image_url\": \"http://...\"). It’s also handy for  “import spreadsheet” type functionality.\n\npaths:\n  /bookings:\n    post:\n      summary: Create a new booking\n      operationId: create-booking\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/Booking'\n          text/csv:\n            schema:\n              type: string\n            example: |\n              departureTime,arrivalTime,operator,price\n              2023-04-01T10:00:00Z,2023-04-01T15:00:00Z,TrainCo,59.99\n      responses:\n        '200':\n          description: Booking created successfully\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Booking'\n            text/csv:\n              schema:\n                type: string\n              example: |\n                bookingId,departureTime,arrivalTime,operator,price\n                123,2023-04-01T10:00:00Z,2023-04-01T15:00:00Z,TrainCo,59.99\n\n\nThe responses are then broken down by status code, and again all the responses can have multiple content types. Then the content can be further described by a schema, and an example (or examples).\n\nFor both request and response, schema is optional, but is massively helpful and worth putting in the work to define, because this is where all of the HTTP body information exists, which can contain validation rules, potential values, examples, and useful context like “why” and “how” instead of just “what”.\n\n\n  Learn more about defining HTTP requests and HTTP responses.\n  Learn more about schemas and data types."
        },
        {
          "id": "openapi-v3.2-understanding-structure-parameter-serialization",
          "title": "Parameter Serialization",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/understanding-structure/parameter-serialization/",
          "content": "Explode\n  Style\n  Path Parameters    \n      Simple\n      Label\n      Matrix\n    \n  \n  Query Parameters    \n      Form\n      Space Delimited\n      Pipe Delimited\n      Deep Object\n    \n  \n  Header Parameters\n  Cookie Parameters\n  Examples and Recommendations    \n      General Guide Lines        \n          Location\n          Style\n        \n      \n      Optional Boolean\n      Required String\n      List of Strings\n      AnyOf Object or String\n    \n  \n\n\nParameters not only define what inputs your API accepts, they also define the format your API expects to receive them in, i.e. how you would like it serialized.\n\nThere are two keywords concerning serialization:\n\nExplode\n\nexplode defines whether parameters should be broken into logical components.\n\nIt takes a boolean value:\n\n  If true; a parameter with multiple values will be serialized as if each of its values were separate parameters.\n    \n      What separates each parameter is determined by the style.\n    \n  \n  If false; a parameter is a single parameter, regardless of how many values it has.\n\n\nIn practice, this means only parameters of type:array or type:object are affected by explode.\n\n  For an array, each value becomes its own parameter.\n  For an object, each key-value pair is concatenated into its own parameter as “key=value”.\n    \n      For any style other than form, if the value is an empty string, then it drops the equals and becomes “key”\n    \n  \n\n\nFor a more verbose description of explode, refer to RFC6750’s Variable Expansion.\n\nIts default value depends on the style of serialization:\n\n  explode:true is the default for style:form\n  explode:false for anything else.\n\n\nStyle\n\nstyle defines how your API expects the parameter to be serialized.\n\nIt takes a string value: The options defined depend on the location your parameter is in:\n\n  in:path defaults to simple but can also be label or matrix.\n  in:query defaults to form but can also be spaceDelimited, pipeDelimited or deepObject.\n  in:header defaults to simple.\n  in:cookie defaults to form.\n\n\nEach style will be explained in more depth per location; examples will make use of the following two parameters.\n\n“pets” which depending on its type has one of the following values:\n\nbool   -&gt; true\nint    -&gt; 2\nstring -&gt; \"dog\"\narray  -&gt; [\"cat\",\"dog\"]\nobject -&gt; {\"age\":2,\"type\":\"dog\"}\n\n\n“hats” which depending on its type has one of the following values:\n\nbool   -&gt; false\nint    -&gt; 1,\nstring -&gt; \"fedora\"\narray  -&gt; [\"fedora\"]\nobject -&gt; {\"type\":\"fedora\"}\n\n\nPath Parameters\n\nFor parameters in:path there are three defined values for style:\n\n\n  simple: defined by RFC6750’s Simple String Expansion.\n  label: defined by RFC6750’s Label Expansion with Dot-Prefix.\n  matrix: defined by RFC6750’s Path-Style Parameter Expansion.\n\n\nThe defaults in:path are:\n\n  style:simple\n  explode:false\n\n\nEvery style in:path follows RFC6750 so the effects of explode are well-defined by RFC6570’s Variable Expansion.\n\nSimple\n\nstyle:simple with its default of explode:false, would serialize your parameters like this:\n\n\n  \n    \n      empty\n      bool\n      int\n      string\n      array\n      object\n    \n  \n  \n    \n       \n      true\n      2\n      dog\n      cat,dog\n      age,2,type,dog\n    \n  \n\n\n\n  Single values are unchanged.\n  An array with multiple values is concatenated into a comma-delimited list.\n  An objecthas its key-value pairs concatenated into comma-delimited pairs, then each pair is concatenated into a comma-delimited list.\n\n\nIf you set explode:true, then the seperator used is also a comma: “,”:\n\n\n  \n    \n      empty\n      bool\n      int\n      string\n      array\n      object\n    \n  \n  \n    \n       \n      true\n      2\n      dog\n      cat,dog\n      age=2,type=dog\n    \n  \n\n\n\n  Single values remain unchanged.\n  Surprisingly, an array with multiple values seems unchanged. Though it treated each value as a separate parameter, it still had to separate them with a comma. So it still ends up as a comma-delimited list.\n  To understand what happened for an object looking back at the rules on explode we see it concatenates key-value pairs into their own parameters as “key=value”. Then it has to separate each parameter with a comma.\n\n\nLabel\n\nstyle:label with its default of explode:false, would  serialize your parameters like this:\n\n\n  \n    \n      empty\n      bool\n      int\n      string\n      array\n      object\n    \n  \n  \n    \n       \n      .true\n      .2\n      .dog\n      .cat,dog\n      .age,2,type,dog\n    \n  \n\n\nEverything is the same as style:simple except all parameters were prefixed with “.”.\n\nIf you set explode:true, then the seperator used is a period: “.”:\n\n\n  \n    \n      empty\n      bool\n      int\n      string\n      array\n      object\n    \n  \n  \n    \n       \n      .true\n      .2\n      .dog\n      .cat.dog\n      .age=2.type=dog\n    \n  \n\n\n\n  Single values remain unchanged.\n  An array becomes a period-delimited list.\n  An object concatenates key-value pairs into their own parameters as “key=value”. Then it separates each parameter with a period.\n\n\nMatrix\n\nstyle:matrix with its default of explode:false, would  serialize your parameters like this:\n\n\n  \n    \n      empty\n      bool\n      int\n      string\n      array\n      object\n    \n  \n  \n    \n       \n      ;pets=true\n      ;pets=2\n      ;pets=dog\n      ;pets=cat,dog\n      ;pets=age,2,type,dog\n    \n  \n\n\nEverything is the same as style:simple except all parameters were prefixed with a semicolon: “;pets=” where “pets” is the parameter’s name.\n\nIf you set explode:true, then the seperator used is a semicolon: “;”.\n\n\n  \n    \n      empty\n      bool\n      int\n      string\n      array\n      object\n    \n  \n  \n    \n       \n      ;pets=true\n      ;pets=2\n      ;pets=dog\n      ;pets=cat;pets=dog\n      ;age=2;type=dog\n    \n  \n\n\n\n  Single values remain unchanged.\n  An array has its values treated as separate parameters. Because they’re now treated separately, every value is prefixed with “;pets=”\n  An object is the exception, it does not get prefixed with “pets=”, but it still has to separated by a semicolon: “;”.\n\n\nQuery Parameters\n\nFor parameters in:query there are four defined values for style.\n\n\n  form: It is defined by RFC6750’s Form-Style Query Expansion, if there are multiple\n  spaceDelimited: An addition by popular demand.\n  pipeDelimited: An addition by popular demand.\n  deepObject: An addition by popular demand.\n\n\nThe defaults in:query are:\n\n  style:form\n  explode:true\n\n\nOnly style:form follows RFC6750 so the effects of explode are only well-defined by RFC6570’s Variable Expansion for style:form.\n\nAn informal, general rule of thumb is:\n\n  Query strings start with a question-mark, this is how you separate the first query parameter from the rest of the URI.\n  Subsequent parameters in:query are separated by an ampersand “&amp;”.\n\n\nJust be aware that spaceDelimited, pipeDelimited and deepObject are not defined by RFC6750.\nThere are caveats to their usage, if you intend to use them, make sure you read their sections carefully.\n\nForm\n\nWith style:form, if you set explode:false, would serialize your parameters like this:\n\n\n  \n    \n      empty\n      bool\n      int\n      string\n      array\n      object\n    \n  \n  \n    \n       \n      ?pets=true\n      ?pets=2\n      ?pets=dog\n      ?pets=cat,dog\n      ?pets=age,2,type,dog\n    \n    \n       \n      ?pets=true&amp;hats=false\n      ?pets=2&amp;hats=1\n      ?pets=dog&amp;hats=fedora\n      ?pets=cat,dog&amp;hats=fedora\n      ?pets=age,2,type,dog&amp;hats=type,fedora\n    \n  \n\n\nYou’ll notice this looks almost identical to style:matrix. \nThere’s only one difference to be\n\n  If it’s the first parameter, the separator from the rest of the URI by a question-mark like above ?pets=true\n  If it’s the second parameter, the separator is an ampersand, you might have a query string like this ?hats=false&amp;pets=true\n\n\nIf you stick with the default of explode:true, then the seperator used is also a comma: “,”:\n\n\n  \n    \n      empty\n      bool\n      int\n      string\n      array\n      object\n    \n  \n  \n    \n       \n      ?pets=true\n      ?pets=2\n      ?pets=dog\n      ?pets=cat&amp;pets=dog\n      ?age=2&amp;type=dog\n    \n    \n       \n      ?pets=true&amp;hats=false\n      ?pets=2&amp;hats=1\n      ?pets=dog&amp;hats=fedora\n      ?pets=cat&amp;pets=dog&amp;hats=fedora\n      ?age=2&amp;type=dog&amp;type=fedora\n    \n  \n\n\nNotice one example is highlighted in red. The OpenAPI Specification states that A Unique Parameter is a combination of name and (in).\nBoth “pets” and “hats” would be considered unique parameters, but they both have the property “type”. When explode is true their properties are serialized as if they were separate parameters. It is as if we have two different parameters both with name:type, in:query, they are no longer unique and one cannot be unambiguously distinguished from the other.\n\nThis conflict is entirely avoided if you explicitly set explode:false on parameters of type:object, but if that’s not an option, remain vigil for possible conflicts.\n\nSpace Delimited\n\nstyle:spaceDelimited with its default of explode:false, would  serialize your parameters like this:\n\n\n  \n    \n      array\n      object\n    \n  \n  \n    \n      ?pets=cat%20dog\n      ?pets=age%202%20type%20dog\n    \n    \n      ?pets=cat%20dog&amp;hats=fedora\n      ?pets=age%202%20type%20dog&amp;hats=type%20fedora\n    \n  \n\n\nIt’s basically identical to style:form with explode:false. The difference being, the separator used is not a comma, but a percent-encoded space “%20”.\n\nYou’ll notice there are no examples for any type that would be a single value. This is because its behaviour is undefined for single values. One could assume it would be identical to style:form, but if your parameter is going to be a single value, there is no need to explicitly define it as spaceDelimited.\n\nstyle:spaceDelimited is not defined by RFC6750 and there is no defined behaviour for explode:true. You could assume it would be identical to the well-defined in:query default of style:form with explode:true. That said, if you’re making that assumption, you’re better off leaving it on the well-defined default.\n\nPipe Delimited\n\nstyle:pipeDelimited with its default of explode:false, would  serialize your parameters like this:\n\n\n  \n    \n      array\n      object\n    \n  \n  \n    \n      ?pets=cat%7Cdog\n      ?pets=age%7C2%7Ctype%7Cdog\n    \n    \n      ?pets=cat%7Cdog&amp;hats=fedora\n      ?pets=age%7C2%7Ctype%7Cdog&amp;hats=type%7Cfedora\n    \n  \n\n\nIt’s basically identical to style:form with explode:false. The difference being, the separator used is not a comma, but a percent-encoded pipe “%7C”.\n\nYou may be able to use a normal pipe “|” but it is not in the list of RFC3986’s Unreserved Characters. As such, it may work in some environments, and not in others.\nIf you still choose to use non-percent-encoded pipes, it would look like this:\n\n\n  \n    \n      array\n      object\n    \n  \n  \n    \n      ?pets=cat|dog\n      ?pets=age|2|type|dog\n    \n    \n      ?pets=cat|dog&amp;hats=fedora\n      ?pets=age|2|type|dog&amp;hats=type|fedora\n    \n  \n\n\nYou’ll notice there are no examples for any type that would be a single value. This is because its behaviour is undefined for single values. One could assume it would be identical to style:form, but if your parameter is going to be a single value, there is no need to explicitly define it as spaceDelimited.\n\nstyle:pipeDelimited is not defined by RFC6750 and there is no defined behaviour for explode:true. You could assume it would be identical to the well-defined in:query default of style:form with explode:true. That said, if you’re making that assumption, you’re better off leaving it on the well-defined default.\n\nDeep Object\n\nstyle:deepObject is undefined for its default of explode:false. You must explicitly specify explode:true for any defined behaviour.\n\nYou may be able to use a normal square brackets “[” and “]” but they are in the list of RFC3986’s Reserved Characters. As such, it may not work in some environments.\n\n\n  \n    \n      object\n    \n  \n  \n    \n      ?pets[age]=2&amp;pets[type]=dog\n    \n    \n      ?pets[age]=2&amp;pets[type]=dog&amp;hats[type]=fedora\n    \n  \n\n\nFor maximum interoperability it is safer to have them percent-encoded:\n\n  “%5B” for “[”\n  “%5D” for “]”.\n\n\n\n  \n    \n      object\n    \n  \n  \n    \n      ?pets%5Bage%5D=2&amp;pets%5Btype%5D=dog\n    \n    \n      ?pets%5Bage%5D=2&amp;pets%5Btype%5D=dog&amp;hats%5Btype%5D=fedora\n    \n  \n\n\nUnsurprisingly, it only has defined behaviour for an object. This style is quite different from any other, even with explode:true the name, key and value are all specified. This makes it useful for avoiding the potential name conflicts objects could cause with style:form, explode:true.\n\nJust bear in mind the name is misleading, despite being called a deepObject, there is no defined behaviour for nested arrays or objects. This is the same for every style in:query.\n\nHeader Parameters\n\nFor parameters in:header there is only one defined value for style: simple.\n\nNaturally, the default value is style:simple, with explode:false.\n\nIt is the same definition as it would be in:path except there is a major caveat to be aware of:\n\n  Headers do not require any percent encoding in the same way a URI string would, so it cannot follow the same definitions laid out by RFC6750.\n\n\nFor this reason it is not recommended to rely on style, explode and schema.\n\nFor parameters in:header it is recommended to make use of the parameter’s content field instead of schema. Then use a media type such as text/plain and require the application to assemble the correct string. This will be the recommended approach as of OpenAPI Version 3.1.1, with more detail available in Appendix D: Serializing Headers and Cookies.\n\nCookie Parameters\n\nFor parameters in:cookie there is only one defined value for style: form.\n\nNaturally, the default value is style:form, with explode:true.\n\nIt is the same definition as it would be in:query except there are several major caveats to be aware of:\n\n  Cookies do not require any percent encoding in the same way a URI string would, so it cannot follow the same definitions laid out by RFC6750.\n  The first parameter is not prefixed with a question-mark “?” like it would in:query.\n  Any subsequent parameters are not separated by an ampersand “&amp;” like they would in:query.\n    \n      Subsequent parameters in:cookie are separated by a semicolon followed by a space “; “.\n    \n  \n\n\nAs such style:form in:cookie is somewhat confusing, and less accurate the more parameters you have to serialize. For this reason it is not recommended to rely on style, explode and schema.\n\nFor parameters in:cookie it is recommended to make use of the parameter’s content field instead of schema. Then use a media type such as text/plain and require the application to assemble the correct string. This will be the recommended approach as of OpenAPI Version 3.1.1, with more detail available in Appendix D: Serializing Headers and Cookies.\n\nExamples and Recommendations\n\nGeneral Guide Lines\n\nLocation\n\nIf a parameter is needed across many paths, or contains sensitive information; it may be sensible to include in:header or in:cookie.\n\n  If the parameter needs to persist across sessions, keep it in:cookie.\n\n\nIf a parameter is only needed in specific paths, it may be sensible include in:path or in:query.\n\nIt is easier to provide parameters in a URL. Requiring headers, cookies or a requestBody generally make requests more difficult.\nKeep it simple; if it’s sensible to include a parameter in:path or in:query, do so.\n\nStyle\n\nFor parameters in:path or in:query; the defaults exist for a reason, they’re well-defined, versatile and simple.\n\nFor parameters in:header or in:cookie; the defaults work to an extent, but the variations on their syntax are beyond the scope of the OpenAPI Specification and what can be described through style. The recommended approach is to forgo style and schema in favour of using content with a media type such as text/plain.\n\nOptional Boolean\n\nLooking at the Train Travel API, we can make a GET request to find available trips, based on our criteria:\n\n/trips:\n  get:\n  ...\n  parameters:\n    ...\n    - name: dogs\n    in: query\n    description: Only return trips where dogs are known to be allowed\n    required: false\n    schema:\n      type: boolean\n      default: false\n\n\nThe parameter is simple, it could be formatted anywhere without issue.\n\nIt doesn’t need to persist between sessions, so it doesn’t need to be in:cookie.\nIt’s specific to this path, so there’s not much benefit in sticking it in:header.\nIt’s optional, so it cannot be in:path.\n\nNo style has been mentioned, nor explode. But the parameter is in:query so we know the default is style:form and explode:true. We would expect a URLs like this:\n\nUser without a dog: /trips\nUser with a dog: /trips?dogs=true\n\nWe could set explode:false but Explode has no effect on parameters that are not arrays or objects. This would be extra documentation with no gain, leaving it as the default keeps your specification concise.\n\nWe could not use any other style available to Query Parameters as only style:form can be used with parameters that are not arrays or objects.\n\nRequired String\n\nLooking at the Train Travel API once more, we can get the details of specific bookings:\n\n/bookings/{bookingId}:\n  parameters:\n    - name: bookingId\n      in: path\n      required: true\n      description: The ID of the booking to retrieve.\n      schema:\n        type: string\n        format: uuid\n        example: 1725ff48-ab45-4bb5-9d02-88745177dedb\n  get:\n    ...\n\n\nAgain the parameter is simple, it could be formatted anywhere without issue.\n\nIt doesn’t need to persist between sessions, so it doesn’t need to be in:cookie.\nIt’s specific to this path, so there’s not much benefit in sticking it in:header.\nIt’s required, so it could be in:path or in:query.\n\nBecause parameters in:path are always required:true, it is the most intuitive place to stick a required parameter.\n\nBy default this be style:simple and explode:false, looking like this: /bookings/1725ff48-ab45-4bb5-9d02-88745177dedb\n\nIt could have a different style like so:\n\n\n  style:label : bookings/.1725ff48-ab45-4bb5-9d02-88745177dedb\n  style:matrix : bookings/;bookingId=1725ff48-ab45-4bb5-9d02-88745177dedb\n\n\nList of Strings\n\nWhat if we could filter trips that stop at a specified list of stations?\n\nIt’s specific to this path, so we should keep it in the URL for simplicity. That means in:path or in:query.\nNot every user knows the station they want, they may simply be looking for the closest stop to their actual destination. This parameter should be optional, so it cannot be in:path.\n\nWe could put it in:path and it would look like so:\n\n/trips:\n  get:\n  ...\n    parameters:\n      ...\n      - name: stations\n        in: query\n        description: Only return trips that stop at these stations\n        required: false,\n        schema:\n          type: array\n          items:\n            type: string\n\n\nNow our URL will look like this:\n\n\n  Users with specific station in mind: /trips?stations=gatwick&amp;stations=london\n  Users with only one station in mind: /trips?stations=london\n\n\nAnyOf Object or String\n\n/trips:\n  get:\n  ...\n    parameters:\n      ...\n      - name: station\n        description: Only return trips that stop at your preferred station, if none, use to fallback if provided.\n        in: query\n      style: deepObject\n      explode: true\n      required: false\n      schema:\n        anyOf: \n          - type: object\n            required:\n              - preferred\n            properties:\n              preferred:\n                type: string\n              fallback:\n                type: string\n          - type: string\n\n\nHere I’ve stated that my schema can be anyOf the following: an object or a string, in style:deepObject. You may have spotted the problem already:\n\n\n  If our user specifies an object, this works as expected: /trips?station[preferred]=gatwick&amp;station[fallback]=london.\n  What if our user specifies a string? It’s undefined, deepObject only has defined behaviour for objects.\n\n\nYou cannot apply style on a per-schema basis. Your style needs to work for all possible variations of your parameter.\nIf you intend to use anyOf, allOf or oneOf make doubly sure your choice of style works for every option.\nAs always, the best option is to minimise your use of complex parameters, keep it simple."
        },
        {
          "id": "openapi-v3.1-understanding-structure-http-requests",
          "title": "HTTP Requests",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/understanding-structure/http-requests/",
          "content": "Structure of Request Bodies    \n      Creating a Resource\n      Updating a Resource\n    \n  \n  File Uploads &amp; Multipart Forms\n\n\nAny API handling use-cases more advanced that purely fetching data will need to define a HTTP request body. POST, PATCH, PUT, all allow a HTTP client to send a body: often JSON or XML. This allows for more information to be sent rather than just query parameters, which have limits.\n\nThe request body can be used for:\n\n\n  Creating new resources (e.g.: booking a train ticket)\n  Updating existing resources (e.g.: updating that booking)\n  Uploading files (e.g.: uploading an image to your railcard)\n\n\nStructure of Request Bodies\n\nIn OpenAPI 3.x, the request body is defined using the requestBody object. This object allows you to specify:\n\n\n  The content type (e.g.: application/json, application/xml).\n  The schema that defines the structure of the request body.\n  Whether the request body is required or optional.\n  Descriptions for these requests to add context to API documentation.\n\n\nLet’s consider the Train Travel API, which allows users to book train tickets.\n\nCreating a Resource\n\nWhen a user wants to book a train ticket, they need to send details like the passenger’s name, trip ID, date of travel, and seat preference, which would look a bit like this:\n\npaths:\n  /bookings:\n    post:\n      summary: Book a train ticket\n      description: Endpoint to book a train ticket\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              properties:\n                passenger_name:\n                  type: string\n                  example: \"John Doe\"\n                trip_id:\n                  type: string\n                  example: \"1234\"\n                date:\n                  type: string\n                  format: date\n                  example: \"2024-08-15\"\n                seat_preference:\n                  type: string\n                  enum: [window, aisle, any]\n                  example: \"window\"\n\n\nHere the requestBody object defines two important properties:\n\n\n  \n    required: true - indicates that the request body is mandatory for this operation.\n  \n  \n    content - specifies that the request body should be in application/json format with the following schema.\n  \n\n\nThe schema defines the structure of the request body, including properties like passenger_name, train_id, date, and seat_preference. This can be defined inline like this, or it can use components to share an existing schema and reduce repetition.\n\nUpdating a Resource\n\nIf a user wants to update their booking (e.g.: change the seat preference), the API can define a PUT or PATCH operation, to allow updating the entire booking, or part of the booking respectively. Either way, they need to send the updated data in the request body. Here’s how to define it:\n\npaths:\n  /bookings/{bookingId}:\n    patch:\n      summary: Update a booking\n      description: Endpoint to update an existing booking\n      parameters:\n        - name: bookingId\n          in: path\n          required: true\n          schema:\n            type: string\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              properties:\n                seat_preference:\n                  type: string\n                  enum: [window, aisle, any]\n                  examples: \n                  - aisle\n\n\nHere the PATCH method is used to describe an operation that can update one specific field from an existing booking. The required: true says the requestBody is mandatory, and the only media type defined is application/json so that says the request must be in that format.\n\nThe schema then defines the structure of the request body, which demonstrates that only the seat_preference property can be updated.\n\nIf multiple properties could be updated, you would define all the properties that could be updated, then show off some examples for common use-cases of things users might want to do.\n\nFile Uploads &amp; Multipart Forms\n\nHTTP requests can also cover more advanced scenarios like file uploads and multipart form data, which have their own guides in the advanced section."
        },
        {
          "id": "openapi-v3.1-understanding-structure-http-responses",
          "title": "HTTP Responses",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/understanding-structure/http-responses/",
          "content": "HTTP Status Codes    \n      Status Ranges\n      More about HTTP Status Codes\n    \n  \n  Empty status body\n\n\nHTTP responses outline what an API user could expect to receive in response to a HTTP request. In OpenAPI responses described in the responses object, broken down by expected media-types and status codes.\n\npaths:\n  /health:\n    get:\n      responses:\n        '200':\n          description: OK\n          content:\n            application/json:\n              schema:\n                type: object\n                properties:\n                  healthy:\n                    type: boolean  \n\n\nHere’s an example from the Train Travel API, showing two responses for the same operation, one success and one failure, both defining a JSON response:\n\n  responses:\n\n    '200':\n      description: A list of train stations\n      headers:\n        RateLimit:\n          description: The RateLimit header communicates quota policies.\n          schema:\n            type: string\n            examples:\n              - limit=10, remaining=0, reset=10\n      content:\n        application/json:\n          schema:\n            properties:\n              $ref: '#/components/schemas/Stations'\n\n    '400':\n      description: Bad Request\n      content:\n        application/problem+json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n\n\n\n  These responses contain shared schemas which are referenced via the components section to keep the relevant parts of the example clear, but you can learn more about schemas to see what else could go in there.\n\n\nThe key parts that define a response:\n\ndescription: A short, descriptive text about the response which is mandatory. It explains the meaning of the response in the context of the API operation. This is often just the status code text, so 200 would be “Ok”, 201 would be “Success”, but it can be anything you think makes sense.\n\nheaders: An optional map of headers that can be sent by the response. Each header is itself described by an object, which defines the name as a key, then has an object with a description of its own and a schema to describe the header. then Cache-Controls, RFC headers like RateLimit or custom headers like 'X-Rate-Limit'.\n\ncontent: An optional field that describes the content of the response body. It allows for different media types to be documented, specifying how the body of the response should be formatted. For each media type, you can define a schema and examples, making it clear what the response will look like.\n\nlinks: An optional section that can define hypermedia relations associated with the response. Links can show clients what operations might be related or available to them after receiving the response, essentially guiding them on what they can do next.\n\nThe HTTP response object in OpenAPI allows for detailed documentation of each possible outcome of an API operation, making it easier for developers to understand and handle those responses correctly in their applications.\n\nHTTP Status Codes\n\nHTTP status codes are essential for defining the responses of API operations. Each response in an API operation must include at least one HTTP status code, such as 200 for success or 404 for not found. Typically an operation specifies one successful status code for the “happy path”, and one or more error statuses describing the variety of things that can go wrong.\n\n  responses:\n    '200':\n      description: OK\n    '304':\n      description: Not Modified\n    '400':\n      description: Bad Request\n    '401':\n      description: Unauthorized\n    '403':\n      description: Forbidden\n    '429':\n      description: Too Many Requests\n    '500':\n      description: Internal Server Error\n\n\nHow many status codes you choose to describe is up to you. There is a balance to be found between “only the status codes the API is programmed to emit” and “everything that could possibly ever come out of the API, server, and network components involved” which is going to be different for everyone.\n\nStatus Ranges\n\nOpenAPI allows defining a range of response codes to simplify documentation:\n\n\n  1XX for informational responses\n  2XX for successful responses\n  3XX for redirection messages\n  4XX for client errors\n  5XX for server errors\n\n\nIf a specific code is detailed within a range, that code’s definition takes precedence over the general range definition. Each status code in the documentation requires a description, explaining under what conditions it is returned. Markdown (CommonMark) can be used for these descriptions to include rich text formatting.\n\nMore about HTTP Status Codes\n\nFor more detailed information on HTTP status codes, the OpenAPI Specification defers to RFC 7231 and the IANA Status Code Registry. If a code is defined there, it’s valid to use in your OpenAPI.\n\nIf you’re struggling to remember which HTTP status codes to use for any scenario, HTTP Cats will help you visualize the right choice.\n\nEmpty status body\n\nSome HTTP responses will not have a body. For example 204 No Content is often used after something has been deleted and therefore there is nothing to return. Another common one is 304 Not Modified, which lets clients know they can reuse previous cached responses because nothing has changed on the server.\n\nTo describe HTTP responses with no body in OpenAPI you simply leave the content object out.\n\npaths:\n  /example:\n    get:\n      summary: \"Endpoint with no response body\"\n      responses:\n        '204':\n          description: \"No content to return\"\n          # No 'content' field here\n\n\n\n  If you are using OpenAPI for contract testing then most tools will understand this, but they will get confused if you are omitting content for responses which do actually return content. Make your OpenAPI be more accurate by describing the return body content of anything which does return, and only omitting content for responses which legitimately do not return content."
        },
        {
          "id": "openapi-v3.1-advanced-splitting-documents-with-ref",
          "title": "Splitting OpenAPI Documents with $ref",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/advanced/splitting-documents-with-ref/",
          "content": "OpenAPI Reusable Components\n  AsyncAPI Reusable Components\n  Using $ref with URLs    \n      Benefits of URL $ref\n      Downsides of URL $ref\n    \n  \n  Propagating Changes\n  Tools for Bundling &amp; Splitting\n  Further Reading\n\n\nAfter using OpenAPI for a while, you might notice your description documents have become a rather unwieldy mess of YAML and JSON. You end up with a whole lot of repetition, and this huge mess just loves to trigger merge conflicts as multiple developers change different things but Git seems none the wiser.\n\nYou can avoid this pain by splitting description documents up with $ref, using various reusable components, but how exactly you go about doing that can be a tricky one to work out.\n\nOpenAPI Reusable Components\n\nThe OpenAPI Documentation includes a brilliant example of an API for playing the classic board game Tic Tac Toe.\n\nThis has several parts that are used several times, so instead of copy-pasting everything they’ve defined reusable components for both schemas and parameters.\n\npaths:\n  # Whole board operations\n  /board:\n    get:\n      summary: Get the whole board\n      description: Retrieves the current state of the board and the winner.\n      tags:\n        - Gameplay\n      operationId: get-board\n      responses:\n        \"200\":\n          description: \"OK\"\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/status\"\n  # Single square operations\n  /board/{row}/{column}:\n    parameters:\n      - $ref: \"#/components/parameters/rowParam\"\n      - $ref: \"#/components/parameters/columnParam\"\n    get:\n      # ... Hidden for readability...\n    put:\n      # ... Hidden for readability...\n\ncomponents:\n  parameters:\n    rowParam:\n      description: Board row (vertical coordinate)\n      name: row\n      in: path\n      required: true\n      schema:\n        $ref: \"#/components/schemas/coordinate\"\n    columnParam:\n      description: Board column (horizontal coordinate)\n      name: column\n      in: path\n      required: true\n      schema:\n        $ref: \"#/components/schemas/coordinate\"\n  schemas:\n    errorMessage:\n      type: string\n      maxLength: 256\n      description: A text message describing an error\n    coordinate:\n      type: integer\n      minimum: 1\n      maximum: 3\n      example: 1\n    mark:\n      type: string\n      enum: [\".\", \"X\", \"O\"]\n      description: Possible values for a board square. `.` means empty square.\n      example: \".\"\n    board:\n      type: array\n      maxItems: 3\n      minItems: 3\n      items:\n        type: array\n        maxItems: 3\n        minItems: 3\n        items:\n          $ref: \"#/components/schemas/mark\"\n    winner:\n      type: string\n      enum: [\".\", \"X\", \"O\"]\n      description: Winner of the game. `.` means nobody has won yet.\n      example: \".\"\n    status:\n      type: object\n      properties:\n        winner:\n          $ref: \"#/components/schemas/winner\"\n        board:\n          $ref: \"#/components/schemas/board\"\n\n\n\nThis is not particularly unmanageable, but let’s pretend there is 50 or more endpoints. You could imagine how this one file would be getting a bit much to handle.\n\nHow people split up there files has been completely unique to the developer for a long time, but certain conventions are starting to emerge with tooling leading the way.\n\n├── paths\n│   ├── board.yaml\n│   └── board_{row}_{column}.yaml\n├── components\n│   ├── schemas\n│   │   ├── errorMessage.yaml\n│   │   ├── board.yaml\n│   │   ├── coordinate.yaml\n│   │   ├── status.yaml\n│   │   ├── winner.yaml\n│   │   └── mark.yaml\n│   └── parameters\n│       ├── columnParam.yaml\n│       └── rowParam.yaml\n└── openapi.yaml\n\n\nThis convention splits each type of components into their own subdirectory, and then puts them into their own unique file.\n\nNow the openapi.yaml is a whole lot lighter.\n\nopenapi: 3.1.0\ninfo:\n  title: Tic Tac Toe\n  description: |\n    This API allows writing down marks on a Tic Tac Toe board\n    and requesting the state of the board or of individual squares.\n  version: 1.0.0\ntags:\n  - name: Gameplay\npaths:\n  /board:\n    $ref: paths/board.yaml\n  /board/{row}/{column}:\n    $ref: paths/board_{row}_{column}.yaml\n\n\nThe paths/board.yaml looks like this:\n\nget:\n  summary: Get the whole board\n  description: Retrieves the current state of the board and the winner.\n  tags:\n    - Gameplay\n  operationId: get-board\n  responses:\n    '200':\n      description: OK\n      content:\n        application/json:\n          schema:\n            $ref: ../components/schemas/status.yaml\n\n\nFinally, components/schemas/status.yaml looks like this:\n\ntype: object\nproperties:\n  winner:\n    $ref: ./winner.yaml\n  board:\n    $ref: ./board.yaml\n\n\nAll the filepaths are relative to their current location, and can traverse up and down the filesystem, with the standard ../ to go up a directory.\n\nThe chance of getting a Git conflict when two different developers add two different paths or expanding properties in a schema is now a fair bit smaller. If a conflict does occur, the diff will be a lot less confusing to work out.\n\nOne downside of splitting up components into different documents like this is that it becomes harder to follow API changes, either directly or by looking at files in GitHub. Changing a schema in one document can effect how multiple different endpoints work, and that can caused a bit of confusion. API change management tools like Bump.sh or Optic can help by spotting breaking changes and reporting them on PRs, so that you can easily see problems that could otherwise slip through.\n\nAsyncAPI Reusable Components\n\nAsyncAPI is thankfully the same when it comes to $ref and components, so if your event-driven API is struggling as much as your HTTP API then it’s time to split things up.\n\n  v0/rust/servers/{server_id}/players/{steam_id}/events/banned:\n    description: Channel for notifying a server banned a player\n    parameters:\n      server_id:\n        \"$ref\": \"./components/parameters.json#/server_id\"\n      steam_id:\n        \"$ref\": \"./components/parameters.json#/steam_id\"\n    subscribe:\n      operationId: ServerPlayerBanned\n      message:\n        \"$ref\": \"./components/messages/ServerPlayerBanned.json\"\n\n\nThis example is taken from the Gaming API example projects, and highlights a slightly different approach of using a single parameters.json document and referencing a parameter within that file, instead of using a parameters/ subdirectory with a file for each parameter. You could do either with either OpenAPI or AsyncAPI, it’s a matter of personal preference.\n\nTo learn more about the components keyword in AsyncAPI, head on over to their documentation.\n\nUsing $ref with URLs\n\nFilepaths are not the only way to work with $ref, you can also use URLs.\n\nThis is particularly helpful when you have a “data model” that is shared across multiple APIs or microservices. Perhaps you don’t want each API to define a User, Company, or Payment separately, and get stuck with infinite different variant models.\n\nSimply publish those shared components as JSON, YAML, or both, on a static site or S3 bucket somewhere and let people $ref them into their API.\n\n  responses:\n    '200':\n      description: OK\n      content:\n        application/json:\n          schema:\n            $ref: \"https://schema.example.org/status.json\"\n\n\nBenefits of URL $ref\n\nDoing this has several benefits. Not only can other API teams all work together to make a single repository of all the most command/shared components, but API consumers can use them too.\n\nPerhaps clients want to implement some client-side validation to make sure form submissions are valid before they waste time and carbon emissions going over the wire talking to the API with an invalid request.\n\nSpecifically splitting the “schemas” out is brilliant because its not just helpful for OpenAPI tooling, but for JSON Schema tooling too. There’s even more JSON Schema tooling than OpenAPI tooling so its handy to be able to use both.\n\nDownsides of URL $ref\n\nOne downside you’re probably already thinking of is that doing all of this requires a bit of work. This is jokingly called SpecOps (API descriptions are also known as “specifications”). Setting up deployment pipelines and hosting to make those reusable components available is a faff.\n\nOther complications can appear depending on which tools you’re using. Some tools do not support URLs in $ref, either for security concerns or because the tool maintainers never got around to it. You need to programmatically replace all the $ref’s with URLs to be local refs, and whilst there are tools which can “bundle” your API descriptions up for you, it’s another bit of work, and adds another copy of the API description document to keep track of and keep updated.\n\nFinally there’s authentication. Some people have their API descriptions in a private Git repositories and cannot access it with https://raw.githubusercontent.com/org/repo/main/content/schemas/foo.json because it would need some sort of access token and how’s that going to work? Making a GitHub Action / Continuous Integration step that deploys the API descriptions or schemas to a public S3 bucket or other public static site is probably the best thing to do there.\n\nOthers hide their OpenAPI and AsyncAPI by choice for security reasons, but that’s never made much sense because Stripe, PayPal, Box, GitHub, and plenty of other massive API companies have their API descriptions out in public and nobody has hacked them. APIs should be protected with firewalls and API keys, but OpenAPI and AsyncAPI information can be plastered all over the place. Another vote for the public static site.\n\nThere is an argument for making public APIs public and keeping internal API’s private, and some hosted API documentation tools can help with that, or you can host internal API docs on a different static site that’s only available on the company network. Either way you’ll need to keep your public APIs public, and keep your shared components public, then hide the internal APIs that reference those. That gives you the best of both worlds.\n\nPropagating Changes\n\nUsing tools like Bump.sh you get all the benefits of a tool that understands $ref, but without any of the hassle of needing to bundle documents up.\n\nLike any tool which uses a build step, this has the pro and the con of meaning that documentation is built at a certain point in time. Changes that happen to the $ref’ed resources - whether they’re in another repository, or being pulled in via URL - will take some time to appear in your API.\n\nFor example, if the Widget API is using a shared Company schema via $ref: https://widgets.com/schema/company.json, and company decides to add VAT number as a property, your Widget API documentation is not going to mention that property until your next build.\n\nIs that a good thing or a bad thing? It can be both depending on the scenario, but having changes appear in your API without your knowledge is probably not ideal.\n\nTools for Bundling &amp; Splitting\n\nBundling is usually only needed if you are working with older or strange tools which do not support $ref properly (or at all). If you are working with Bump.sh CLI you won’t need to bundle, but if a tool wants you to import a single openapi.yaml document you might need to bundle.\n\n$ redocly bundle openapi.yaml -o openapi-bundled.yaml\nbundling openapi.yaml...\n📦 Created a bundle for openapi.yaml at openapi-bundled.yaml 105ms.\n\n\nThis will grab all of the $ref’s that use “external files” or URLs and move the contents into the relevant subsection of components in the openapi-bundled.yaml document.\n\nSplitting does the opposite. If you have a massive painful document (maybe generated from HTTP or converted from Postman) you can split it down into multiple documents with a sensible folder structure, ditch the original, commit all that to Git, and push it up to Bump.sh with all the $ref’s intact.\n\nredocly split generated-openapi.json --outDir api/\n\nbump deploy api/openapi.json\n\n\nTo give Redocly CLI a try, in combination with the Bump CLI, install them both:\n\nnpm install -g @redocly/cli bump-cli\n\n\nFurther Reading\n\nIf you’d like to learn more than you could ever possibly want to know about AsyncAPI $ref then head on over to The Reference Rabbit Hole by Jonas Lagoni."
        },
        {
          "id": "openapi-v3.1-data-models-examples",
          "title": "Examples & Defaults",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/data-models/examples/",
          "content": "Examples    \n      Schema Examples\n      Media Type Examples\n      Parameter Examples\n      When to use which\n      Example of All Examples\n    \n  \n  Defaults\n\n\nExamples and defaults are a change to demonstrate some potential input and output values. Examples are particularly useful at showing off parameters, headers, requests, responses, and various other bits of OpenAPI, which is handy for creating API documentation. These They can also be read by tools and libraries for other purposes, like an API mocking tool can use sample values to generate mock requests.\n\nExamples\n\nThere are three main types of examples:\n\n\n  Schema Examples\n  Media Type Examples\n  Parameter Examples\n\n\nSchema Examples\n\nThe schema object is used all over in OpenAPI, for requests and responses, parameters, and headers. A schema in OpenAPI v3.1 can add an example for an entire schema, part of a schema, or a single specific property, which is either confusing or flexible, depending on how you see the world.\n\nHere’s an example of the examples keyword being used for specific properties inside an object.\n\nresponses:\n  '200':\n    description: 'OK'\n    content:\n      application/json:\n        schema:\n          properties: \n          id:\n            type: integer\n          name:\n            type: string\n            examples: \n            - Dave\n          completed: \n            type: boolean\n            examples: \n            - false\n          completed_at:\n            type: ['string', 'null']\n            format: date-time\n            examples: \n            - '2024-04-23T13:22:52.685Z'\n          required:\n          - id\n          - name\n          - completed\n\n\nThis uses the JSON Schema keyword examples to provide an examples. Seeing as these examples are an array of values, the YAML - syntax is used as an array of one, but you can provide multiple examples if you like.\n\n\n  In OpenAPI v3.0 you may have used the example keyword with a single value, but this was deprecated in OpenAPI v3.1 and whilst it is generally still supported it is recommended you use the examples keyword.\n\n\nHere’s an example of the same schema examples keyword being used to provide an example for an entire object.\n\nresponses:\n  \"200\":\n    description: OK\n    content:\n      application/json:\n        schema:\n          properties:\n            id:\n              type: integer\n            name:\n              type: string\n            completed:\n              type: boolean\n            completed_at:\n              type: ['string', 'null']\n              format: date-time\n          required:\n            - id\n            - name\n            - completed\n          examples:\n            - id: 2\n              name: Dave\n              completed: true\n              completed_at: 2024-04-23T13:22:52.685Z\n\n\nSame exact concept, but instead of being put on the property it’s being put on the entire object at the root of the schema.\n\nYou can mix and match property and object examples as much as you like, and most tooling will know how to pick the most appropriate example for any given scenario.\n\nFor more on these schema examples, head over to the Learn JSON Schema: Examples.\n\nMedia Type Examples\n\nThe Media Type Object is the same object that defines the request body and each response underneath the application/json or whichever other content type is being defined.\n\nThis type of example allows you to create an entire request or response example, and you have a choice between two two keywords: example or examples. There is more than just an s difference between these keywords, they’re different shapes too. example is singular example which just contains the actual example value.\n\nresponses:\n  \"200\":\n    description: OK\n    content:\n      application/json:\n        example:\n          id: 1\n          name: get food\n          completed: false\n        schema:\n          properties:\n            id:\n              type: integer\n            name:\n              type: string\n            completed:\n              type: boolean\n            completed_at:\n              type: string\n              format: date-time\n              nullable: true\n          required:\n            - id\n            - name\n            - completed\n\n\nHowever, examples is an array of objects, which have an arbitrary string which acts as a variable name for that example, and that property is another object which contains several optional properties including a value property, which then contains the actual example.\n\nresponses:\n  \"200\":\n    content:\n      application/json:\n        examples:\n          incompleteTask:\n            summary: Incomplete Task\n            value:\n              id: 1\n              name: get food\n              completed: false\n          completeTask:\n            summary: Complete Task\n            value:\n              id: 2\n              name: get cider\n              completed: true\n              completed_at: 2020-08-23T13:22:52.685Z\n        schema:\n          properties:\n            id:\n              type: integer\n            name:\n              type: string\n            completed:\n              type: boolean\n            completed_at:\n              type: string\n              format: date-time\n              nullable: true\n          required:\n            - id\n            - name\n            - completed\n\n\nUsing named examples like this allows for more clarity when certain combinations of parameters might be grouped together. For example if you support polymorphism for different types of objects for a payment accepting both a Bank Account and Credit Card, you could show how requests and responses look for those and let the user pick between them in documentation.\n\nThe example names are entirely arbitrary, and casing does not matter, but it’s best to use something more like a variable name with no special characters as these names are used in the URL for docs, and used in various programmatic ways for docs.\n\nThe summary name is optional, but is a great place to put human readable names in that can then show up in API documentation tools.\n\n\n\nNotice that these examples are all defined next to the schema keyword, not inside it. Examples outside the schema object are an object with names, examples inside the schema object are just a list (array) which have no names. For clarity you can check the OpenAPI v3.1 Specification, looking at the Media Type Object and the Schema Object.\n\nHere’s a quick example of all the examples so you know where to start.\n\nrequestBody:\n  content:\n    application/json:\n      schema:      # schema object\n        examples:  # schema examples\n          # ...\n\n      example:     # media type example\n        # ...\n    \n      examples:    # media type examples\n        someName:\n          summary: ...\n          value:\n            # ...\n\n\nParameter Examples\n\nThe OpenAPI v3.1 Parameter Object describes path parameters, query parameters, headers, etc. Since OpenAPI v3.0 They can have examples or an example, which work the same as the media type examples. They can also have a schema, which means they can have schema examples just like we talked about above.\n\n/params:\n  get:\n    parameters:\n      - name: single-example-good\n        description: Valid to its schema\n        in: query\n        schema: \n          type: string\n          enum: [foo, bar]\n        example: foo\n\n      - name: single-schema-example-good\n        description: Valid to its schema\n        in: query\n        schema: \n          type: string\n          enum: [foo, bar]\n          example: foo\n\n      - name: multiple-examples\n        description: Some valid to its schema some not\n        in: query\n        schema: \n          type: string\n          enum: [foo, bar]\n        examples: \n          the-good:\n            summary: The Good\n            value: foo\n          the-bad:\n            summary: The Bad\n            value: 123\n          the-ugly:\n            summary: The Ugly\n            value: [an, array]\n\n\nThis is a lot of different types of example to think about, so how can we break it down?\n\nWhen to use which\n\nWhen you get the hang of when to use what sort of examples they can be really powerful. Here are a few tips:\n\n\n  Schema examples on properties can be really helpful to make sure that wherever a schema is referenced it is going to make some sense.\n  Media Type examples can then optionally be added to help with mocking, and documenting more complex APIs if the computed schema examples are not good enough.\n  Parameter examples don’t particularly matter how you do it, especially if its all being defined inline (not using $ref) so do whichever.\n\n\nSome mocking tools like Microcks might prefer you use named examples, and match up your parameters, requests, and responses to help match up expected inputs with matching outputs, but that is not something you need to think about unless you are planning to use those tools.\n\nExample of All Examples\n\nLet’s go on an adventure through all the types of examples available in OpenAPI v3.1, with a bunch of Adventure Time characters with random coordinates of their last known locations.\n\nopenapi: 3.1.0\ninfo:\n  title: Example of All Examples\n  version: 1.0.0\npaths:\n  /all-the-examples:\n      get:\n        operationId: infinite-examples\n        responses:\n          \"200\":\n            description: OK\n            content:\n              application/json:\n                schema:\n                  properties:\n                    name:\n                      type: string\n                      # Schema Object Example (Deprecated)\n                      example: Finn\n                    coordinates:\n                      description: We couldn't pick a format for coordinates so we support\n                      pretty much all of them.\n                      # Schema Object Examples\n                      examples:\n                      - \"52.378091, 4.899207\"\n                      - [52.378091, 4.899207]\n                      - { lat: 52.378091,, lon: 4.899207 }\n                  required:\n                    - name\n                    - coordinates\n                  \n                  # Schema Object Example (for an object)\n                  example:\n                    name: Jake\n                    coordinates: \"52.378082, 4.899218\"\n\n                # Media Type Example\n                example:\n                  name: Princess Bubblegum\n                  coordinates: \"51.20180, 3.22488\"\n\n                # Media Type Examples\n                # cannot have this and the OpenAPI Media Type Example together\n                examples:\n                  ice-king:\n                    value:\n                      name: Ice King\n                      coordinates: \"78.21757, 15.63699\"\n\n\nHopefully this will help you create useful examples that can be used by all sorts of tooling.\n\n\n  If you are working with generated OpenAPI documents that you cannot edit, you can use Overlays to add the examples in later.\n\n\nDefaults\n\nThere’s one more thing to consider: sometimes an example is not needed, because a default is more appropriate as a validation rule which then can also be used as an example.\n\nIn the schema object examples we had this property:\n\n  schema:\n    properties:\n      completed: \n        type: boolean\n        examples: \n        - false\n\n\nA boolean has two options, true and false, so an example of that seems redundant.\n\nWhat are we trying to achieve in doing this? We want the docs and mocks to have a useful value to work with, but most tools know what to do here just from the boolean alone, so we could remove the example, or we could do something more useful.\n\n  schema:\n    properties:\n      completed: \n        type: boolean\n        default: false\n\n\nThis lets tools know that false is the default state for this property, which will make documentation more clear, help mock servers act more consistently, and can even make any code generated from OpenAPI work as expected.\n\nThe default keyword is therefore quite similar to examples within a schema object, as it can introduce concrete values into the schema which can be used for all sorts of tooling, but it’s more functional."
        },
        {
          "id": "openapi-v3.2-advanced-splitting-documents-with-ref",
          "title": "Splitting OpenAPI Documents with $ref",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/advanced/splitting-documents-with-ref/",
          "content": "OpenAPI Reusable Components\n  AsyncAPI Reusable Components\n  Using $ref with URLs    \n      Benefits of URL $ref\n      Downsides of URL $ref\n    \n  \n  Propagating Changes\n  Tools for Bundling &amp; Splitting\n  Further Reading\n\n\nAfter using OpenAPI for a while, you might notice your description documents have become a rather unwieldy mess of YAML and JSON. You end up with a whole lot of repetition, and this huge mess just loves to trigger merge conflicts as multiple developers change different things but Git seems none the wiser.\n\nYou can avoid this pain by splitting description documents up with $ref, using various reusable components, but how exactly you go about doing that can be a tricky one to work out.\n\nOpenAPI Reusable Components\n\nThe OpenAPI Documentation includes a brilliant example of an API for playing the classic board game Tic Tac Toe.\n\nThis has several parts that are used several times, so instead of copy-pasting everything they’ve defined reusable components for both schemas and parameters.\n\npaths:\n  # Whole board operations\n  /board:\n    get:\n      summary: Get the whole board\n      description: Retrieves the current state of the board and the winner.\n      tags:\n        - gameplay\n      operationId: get-board\n      responses:\n        \"200\":\n          description: \"OK\"\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/status\"\n  # Single square operations\n  /board/{row}/{column}:\n    parameters:\n      - $ref: \"#/components/parameters/rowParam\"\n      - $ref: \"#/components/parameters/columnParam\"\n    get:\n      # ... Hidden for readability...\n    put:\n      # ... Hidden for readability...\n\ncomponents:\n  parameters:\n    rowParam:\n      description: Board row (vertical coordinate)\n      name: row\n      in: path\n      required: true\n      schema:\n        $ref: \"#/components/schemas/coordinate\"\n    columnParam:\n      description: Board column (horizontal coordinate)\n      name: column\n      in: path\n      required: true\n      schema:\n        $ref: \"#/components/schemas/coordinate\"\n  schemas:\n    errorMessage:\n      type: string\n      maxLength: 256\n      description: A text message describing an error\n    coordinate:\n      type: integer\n      minimum: 1\n      maximum: 3\n      example: 1\n    mark:\n      type: string\n      enum: [\".\", \"X\", \"O\"]\n      description: Possible values for a board square. `.` means empty square.\n      example: \".\"\n    board:\n      type: array\n      maxItems: 3\n      minItems: 3\n      items:\n        type: array\n        maxItems: 3\n        minItems: 3\n        items:\n          $ref: \"#/components/schemas/mark\"\n    winner:\n      type: string\n      enum: [\".\", \"X\", \"O\"]\n      description: Winner of the game. `.` means nobody has won yet.\n      example: \".\"\n    status:\n      type: object\n      properties:\n        winner:\n          $ref: \"#/components/schemas/winner\"\n        board:\n          $ref: \"#/components/schemas/board\"\n\n\n\nThis is not particularly unmanageable, but let’s pretend there is 50 or more endpoints. You could imagine how this one file would be getting a bit much to handle.\n\nHow people split up there files has been completely unique to the developer for a long time, but certain conventions are starting to emerge with tooling leading the way.\n\n├── paths\n│   ├── board.yaml\n│   └── board_{row}_{column}.yaml\n├── components\n│   ├── schemas\n│   │   ├── errorMessage.yaml\n│   │   ├── board.yaml\n│   │   ├── coordinate.yaml\n│   │   ├── status.yaml\n│   │   ├── winner.yaml\n│   │   └── mark.yaml\n│   └── parameters\n│       ├── columnParam.yaml\n│       └── rowParam.yaml\n└── openapi.yaml\n\n\nThis convention splits each type of components into their own subdirectory, and then puts them into their own unique file.\n\nNow the openapi.yaml is a whole lot lighter.\n\nopenapi: 3.2.0\ninfo:\n  title: Tic Tac Toe\n  description: |\n    This API allows writing down marks on a Tic Tac Toe board\n    and requesting the state of the board or of individual squares.\n  version: 1.0.0\ntags:\n  - name: gameplay\npaths:\n  /board:\n    $ref: paths/board.yaml\n  /board/{row}/{column}:\n    $ref: paths/board_{row}_{column}.yaml\n\n\nThe paths/board.yaml looks like this:\n\nget:\n  summary: Get the whole board\n  description: Retrieves the current state of the board and the winner.\n  tags:\n    - gameplay\n  operationId: get-board\n  responses:\n    '200':\n      description: OK\n      content:\n        application/json:\n          schema:\n            $ref: ../components/schemas/status.yaml\n\n\nFinally, components/schemas/status.yaml looks like this:\n\ntype: object\nproperties:\n  winner:\n    $ref: ./winner.yaml\n  board:\n    $ref: ./board.yaml\n\n\nAll the filepaths are relative to their current location, and can traverse up and down the filesystem, with the standard ../ to go up a directory.\n\nThe chance of getting a Git conflict when two different developers add two different paths or expanding properties in a schema is now a fair bit smaller. If a conflict does occur, the diff will be a lot less confusing to work out.\n\nOne downside of splitting up components into different documents like this is that it becomes harder to follow API changes, either directly or by looking at files in GitHub. Changing a schema in one document can effect how multiple different endpoints work, and that can caused a bit of confusion. API change management tools like Bump.sh or Optic can help by spotting breaking changes and reporting them on PRs, so that you can easily see problems that could otherwise slip through.\n\nAsyncAPI Reusable Components\n\nAsyncAPI is thankfully the same when it comes to $ref and components, so if your event-driven API is struggling as much as your HTTP API then it’s time to split things up.\n\n  v0/rust/servers/{server_id}/players/{steam_id}/events/banned:\n    description: Channel for notifying a server banned a player\n    parameters:\n      server_id:\n        \"$ref\": \"./components/parameters.json#/server_id\"\n      steam_id:\n        \"$ref\": \"./components/parameters.json#/steam_id\"\n    subscribe:\n      operationId: ServerPlayerBanned\n      message:\n        \"$ref\": \"./components/messages/ServerPlayerBanned.json\"\n\n\nThis example is taken from the Gaming API example projects, and highlights a slightly different approach of using a single parameters.json document and referencing a parameter within that file, instead of using a parameters/ subdirectory with a file for each parameter. You could do either with either OpenAPI or AsyncAPI, it’s a matter of personal preference.\n\nTo learn more about the components keyword in AsyncAPI, head on over to their documentation.\n\nUsing $ref with URLs\n\nFilepaths are not the only way to work with $ref, you can also use URLs.\n\nThis is particularly helpful when you have a “data model” that is shared across multiple APIs or microservices. Perhaps you don’t want each API to define a User, Company, or Payment separately, and get stuck with infinite different variant models.\n\nSimply publish those shared components as JSON, YAML, or both, on a static site or S3 bucket somewhere and let people $ref them into their API.\n\n  responses:\n    '200':\n      description: OK\n      content:\n        application/json:\n          schema:\n            $ref: \"https://schema.example.org/status.json\"\n\n\nBenefits of URL $ref\n\nDoing this has several benefits. Not only can other API teams all work together to make a single repository of all the most command/shared components, but API consumers can use them too.\n\nPerhaps clients want to implement some client-side validation to make sure form submissions are valid before they waste time and carbon emissions going over the wire talking to the API with an invalid request.\n\nSpecifically splitting the “schemas” out is brilliant because its not just helpful for OpenAPI tooling, but for JSON Schema tooling too. There’s even more JSON Schema tooling than OpenAPI tooling so its handy to be able to use both.\n\nDownsides of URL $ref\n\nOne downside you’re probably already thinking of is that doing all of this requires a bit of work. This is jokingly called SpecOps (API descriptions are also known as “specifications”). Setting up deployment pipelines and hosting to make those reusable components available is a faff.\n\nOther complications can appear depending on which tools you’re using. Some tools do not support URLs in $ref, either for security concerns or because the tool maintainers never got around to it. You need to programmatically replace all the $ref’s with URLs to be local refs, and whilst there are tools which can “bundle” your API descriptions up for you, it’s another bit of work, and adds another copy of the API description document to keep track of and keep updated.\n\nFinally there’s authentication. Some people have their API descriptions in a private Git repositories and cannot access it with https://raw.githubusercontent.com/org/repo/main/content/schemas/foo.json because it would need some sort of access token and how’s that going to work? Making a GitHub Action / Continuous Integration step that deploys the API descriptions or schemas to a public S3 bucket or other public static site is probably the best thing to do there.\n\nOthers hide their OpenAPI and AsyncAPI by choice for security reasons, but that’s never made much sense because Stripe, PayPal, Box, GitHub, and plenty of other massive API companies have their API descriptions out in public and nobody has hacked them. APIs should be protected with firewalls and API keys, but OpenAPI and AsyncAPI information can be plastered all over the place. Another vote for the public static site.\n\nThere is an argument for making public APIs public and keeping internal API’s private, and some hosted API documentation tools can help with that, or you can host internal API docs on a different static site that’s only available on the company network. Either way you’ll need to keep your public APIs public, and keep your shared components public, then hide the internal APIs that reference those. That gives you the best of both worlds.\n\nPropagating Changes\n\nUsing tools like Bump.sh you get all the benefits of a tool that understands $ref, but without any of the hassle of needing to bundle documents up.\n\nLike any tool which uses a build step, this has the pro and the con of meaning that documentation is built at a certain point in time. Changes that happen to the $ref’ed resources - whether they’re in another repository, or being pulled in via URL - will take some time to appear in your API.\n\nFor example, if the Widget API is using a shared Company schema via $ref: https://widgets.com/schema/company.json, and company decides to add VAT number as a property, your Widget API documentation is not going to mention that property until your next build.\n\nIs that a good thing or a bad thing? It can be both depending on the scenario, but having changes appear in your API without your knowledge is probably not ideal.\n\nTools for Bundling &amp; Splitting\n\nBundling is usually only needed if you are working with older or strange tools which do not support $ref properly (or at all). If you are working with Bump.sh CLI you won’t need to bundle, but if a tool wants you to import a single openapi.yaml document you might need to bundle.\n\n$ redocly bundle openapi.yaml -o openapi-bundled.yaml\nbundling openapi.yaml...\n📦 Created a bundle for openapi.yaml at openapi-bundled.yaml 105ms.\n\n\nThis will grab all of the $ref’s that use “external files” or URLs and move the contents into the relevant subsection of components in the openapi-bundled.yaml document.\n\nSplitting does the opposite. If you have a massive painful document (maybe generated from HTTP or converted from Postman) you can split it down into multiple documents with a sensible folder structure, ditch the original, commit all that to Git, and push it up to Bump.sh with all the $ref’s intact.\n\nredocly split generated-openapi.json --outDir api/\n\nbump deploy api/openapi.json\n\n\nTo give Redocly CLI a try, in combination with the Bump CLI, install them both:\n\nnpm install -g @redocly/cli bump-cli\n\n\nFurther Reading\n\nIf you’d like to learn more than you could ever possibly want to know about AsyncAPI $ref then head on over to The Reference Rabbit Hole by Jonas Lagoni."
        },
        {
          "id": "openapi-v3.2-data-models-examples",
          "title": "Examples & Defaults",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/data-models/examples/",
          "content": "Examples    \n      Schema Examples\n      Media Type Examples\n      Parameter Examples\n      When to use which\n      Example of All Examples\n    \n  \n  Defaults\n\n\nExamples and defaults are a change to demonstrate some potential input and output values. Examples are particularly useful at showing off parameters, headers, requests, responses, and various other bits of OpenAPI, which is handy for creating API documentation. These They can also be read by tools and libraries for other purposes, like an API mocking tool can use sample values to generate mock requests.\n\nExamples\n\nThere are three main types of examples:\n\n\n  Schema Examples\n  Media Type Examples\n  Parameter Examples\n\n\nSchema Examples\n\nThe schema object is used all over in OpenAPI, for requests and responses, parameters, and headers. A schema in OpenAPI v3.1 can add an example for an entire schema, part of a schema, or a single specific property, which is either confusing or flexible, depending on how you see the world.\n\nHere’s an example of the examples keyword being used for specific properties inside an object.\n\nresponses:\n  '200':\n    description: 'OK'\n    content:\n      application/json:\n        schema:\n          properties: \n          id:\n            type: integer\n          name:\n            type: string\n            examples: \n            - Dave\n          completed: \n            type: boolean\n            examples: \n            - false\n          completed_at:\n            type: ['string', 'null']\n            format: date-time\n            examples: \n            - '2024-04-23T13:22:52.685Z'\n          required:\n          - id\n          - name\n          - completed\n\n\nThis uses the JSON Schema keyword examples to provide an examples. Seeing as these examples are an array of values, the YAML - syntax is used as an array of one, but you can provide multiple examples if you like.\n\n\n  In OpenAPI v3.0 you may have used the example keyword with a single value, but this was deprecated in OpenAPI v3.1 and whilst it is generally still supported it is recommended you use the examples keyword.\n\n\nHere’s an example of the same schema examples keyword being used to provide an example for an entire object.\n\nresponses:\n  \"200\":\n    description: OK\n    content:\n      application/json:\n        schema:\n          properties:\n            id:\n              type: integer\n            name:\n              type: string\n            completed:\n              type: boolean\n            completed_at:\n              type: ['string', 'null']\n              format: date-time\n          required:\n            - id\n            - name\n            - completed\n          examples:\n            - id: 2\n              name: Dave\n              completed: true\n              completed_at: 2024-04-23T13:22:52.685Z\n\n\nSame exact concept, but instead of being put on the property it’s being put on the entire object at the root of the schema.\n\nYou can mix and match property and object examples as much as you like, and most tooling will know how to pick the most appropriate example for any given scenario.\n\nFor more on these schema examples, head over to the Learn JSON Schema: Examples.\n\nMedia Type Examples\n\nThe Media Type Object is the same object that defines the request body and each response underneath the application/json or whichever other content type is being defined.\n\nThis type of example allows you to create an entire request or response example, and you have a choice between two two keywords: example or examples. There is more than just an s difference between these keywords, they’re different shapes too. example is singular example which just contains the actual example value.\n\nresponses:\n  \"200\":\n    description: OK\n    content:\n      application/json:\n        example:\n          id: 1\n          name: get food\n          completed: false\n        schema:\n          properties:\n            id:\n              type: integer\n            name:\n              type: string\n            completed:\n              type: boolean\n            completed_at:\n              type: string\n              format: date-time\n              nullable: true\n          required:\n            - id\n            - name\n            - completed\n\n\nHowever, examples is an array of objects, which have an arbitrary string which acts as a variable name for that example, and that property is another object which contains several optional properties including a value property, which then contains the actual example.\n\nresponses:\n  \"200\":\n    content:\n      application/json:\n        examples:\n          incompleteTask:\n            summary: Incomplete Task\n            value:\n              id: 1\n              name: get food\n              completed: false\n          completeTask:\n            summary: Complete Task\n            value:\n              id: 2\n              name: get cider\n              completed: true\n              completed_at: 2020-08-23T13:22:52.685Z\n        schema:\n          properties:\n            id:\n              type: integer\n            name:\n              type: string\n            completed:\n              type: boolean\n            completed_at:\n              type: string\n              format: date-time\n              nullable: true\n          required:\n            - id\n            - name\n            - completed\n\n\nUsing named examples like this allows for more clarity when certain combinations of parameters might be grouped together. For example if you support polymorphism for different types of objects for a payment accepting both a Bank Account and Credit Card, you could show how requests and responses look for those and let the user pick between them in documentation.\n\nThe example names are entirely arbitrary, and casing does not matter, but it’s best to use something more like a variable name with no special characters as these names are used in the URL for docs, and used in various programmatic ways for docs.\n\nThe summary name is optional, but is a great place to put human readable names in that can then show up in API documentation tools.\n\n\n\nNotice that these examples are all defined next to the schema keyword, not inside it. Examples outside the schema object are an object with names, examples inside the schema object are just a list (array) which have no names. For clarity you can check the OpenAPI v3.1 Specification, looking at the Media Type Object and the Schema Object.\n\nHere’s a quick example of all the examples so you know where to start.\n\nrequestBody:\n  content:\n    application/json:\n      schema:      # schema object\n        examples:  # schema examples\n          # ...\n\n      example:     # media type example\n        # ...\n    \n      examples:    # media type examples\n        someName:\n          summary: ...\n          value:\n            # ...\n\n\nParameter Examples\n\nThe OpenAPI v3.1 Parameter Object describes path parameters, query parameters, headers, etc. Since OpenAPI v3.0 They can have examples or an example, which work the same as the media type examples. They can also have a schema, which means they can have schema examples just like we talked about above.\n\n/params:\n  get:\n    parameters:\n      - name: single-example-good\n        description: Valid to its schema\n        in: query\n        schema: \n          type: string\n          enum: [foo, bar]\n        example: foo\n\n      - name: single-schema-example-good\n        description: Valid to its schema\n        in: query\n        schema: \n          type: string\n          enum: [foo, bar]\n          example: foo\n\n      - name: multiple-examples\n        description: Some valid to its schema some not\n        in: query\n        schema: \n          type: string\n          enum: [foo, bar]\n        examples: \n          the-good:\n            summary: The Good\n            value: foo\n          the-bad:\n            summary: The Bad\n            value: 123\n          the-ugly:\n            summary: The Ugly\n            value: [an, array]\n\n\nThis is a lot of different types of example to think about, so how can we break it down?\n\nWhen to use which\n\nWhen you get the hang of when to use what sort of examples they can be really powerful. Here are a few tips:\n\n\n  Schema examples on properties can be really helpful to make sure that wherever a schema is referenced it is going to make some sense.\n  Media Type examples can then optionally be added to help with mocking, and documenting more complex APIs if the computed schema examples are not good enough.\n  Parameter examples don’t particularly matter how you do it, especially if its all being defined inline (not using $ref) so do whichever.\n\n\nSome mocking tools like Microcks might prefer you use named examples, and match up your parameters, requests, and responses to help match up expected inputs with matching outputs, but that is not something you need to think about unless you are planning to use those tools.\n\nExample of All Examples\n\nLet’s go on an adventure through all the types of examples available in OpenAPI v3.1, with a bunch of Adventure Time characters with random coordinates of their last known locations.\n\nopenapi: 3.2.0\ninfo:\n  title: Example of All Examples\n  version: 1.0.0\npaths:\n  /all-the-examples:\n      get:\n        operationId: infinite-examples\n        responses:\n          \"200\":\n            description: OK\n            content:\n              application/json:\n                schema:\n                  properties:\n                    name:\n                      type: string\n                      # Schema Object Example (Deprecated)\n                      example: Finn\n                    coordinates:\n                      description: We couldn't pick a format for coordinates so we support\n                      pretty much all of them.\n                      # Schema Object Examples\n                      examples:\n                      - \"52.378091, 4.899207\"\n                      - [52.378091, 4.899207]\n                      - { lat: 52.378091,, lon: 4.899207 }\n                  required:\n                    - name\n                    - coordinates\n                  \n                  # Schema Object Example (for an object)\n                  example:\n                    name: Jake\n                    coordinates: \"52.378082, 4.899218\"\n\n                # Media Type Example\n                example:\n                  name: Princess Bubblegum\n                  coordinates: \"51.20180, 3.22488\"\n\n                # Media Type Examples\n                # cannot have this and the OpenAPI Media Type Example together\n                examples:\n                  ice-king:\n                    value:\n                      name: Ice King\n                      coordinates: \"78.21757, 15.63699\"\n\n\nHopefully this will help you create useful examples that can be used by all sorts of tooling.\n\n\n  If you are working with generated OpenAPI documents that you cannot edit, you can use Overlays to add the examples in later.\n\n\nDefaults\n\nThere’s one more thing to consider: sometimes an example is not needed, because a default is more appropriate as a validation rule which then can also be used as an example.\n\nIn the schema object examples we had this property:\n\n  schema:\n    properties:\n      completed: \n        type: boolean\n        examples: \n        - false\n\n\nA boolean has two options, true and false, so an example of that seems redundant.\n\nWhat are we trying to achieve in doing this? We want the docs and mocks to have a useful value to work with, but most tools know what to do here just from the boolean alone, so we could remove the example, or we could do something more useful.\n\n  schema:\n    properties:\n      completed: \n        type: boolean\n        default: false\n\n\nThis lets tools know that false is the default state for this property, which will make documentation more clear, help mock servers act more consistently, and can even make any code generated from OpenAPI work as expected.\n\nThe default keyword is therefore quite similar to examples within a schema object, as it can introduce concrete values into the schema which can be used for all sorts of tooling, but it’s more functional."
        },
        {
          "id": "openapi-v3.1-data-models-json-schema",
          "title": "JSON Schema in OpenAPI",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/data-models/json-schema/",
          "content": "JSON Schema Documentation\n  JSON Schema Specifications\n  Extra OpenAPI-specific Properties\n\n\nFor a long time JSON Schema and OpenAPI Schema Objects were similar but different. OpenAPI was inspired by JSON Schema, then they both evolved separately, but finally in OpenAPI v3.1 with lots of work from both teams, the specifications realigned on JSON Schema Draft 2020-12. This means you can learn more about OpenAPI Schema Objects by learning more about JSON Schema.\n\nJSON Schema Documentation\n\nThe best places to start learning about JSON Schema is via the documentation, which comes in a few forms.\n\n\n  JSON Schema Tutorials - Getting started guides, and tutorials focusing in on particular bits like the differences between required and optional properties, nesting schemas, using composition with anyOf, allOf, and oneOf, etc.\n  JSON Schema Glossary - There’s a lot of terminology to wrap your head around depending on how deep you want to go, such as dialect, vocabulary, instance, subschema, composition, etc. This page explains it all and links off to more information on each.\n  Lean JSON Schema - A mixture between specification and tutorial, with lots of examples explaining how various keywords work.\n\n\nThese resources are intended to help the general user not need to go and dive into the specifications straight away, as these are more technical documents aimed more at tooling developers. If you cannot find what you need to know in the documentation then you might end up reading the specifications, so where can those be found?\n\nJSON Schema Specifications\n\nIn OpenAPI v3.1 the Schema Object is defined as a superset of the JSON Schema Specification Draft 2020-12, which is split across two relevant specifications.\n\n\n  JSON Schema Core - defines the basic foundation of JSON Schema.\n  JSON Schema Validation - defines the validation keywords of JSON Schema.\n\n\n\n  Unless stated otherwise, the property definitions follow those of JSON Schema and do not add any additional semantics. Where JSON Schema indicates that behavior is defined by the application (e.g. for annotations), OAS also defers the definition of semantics to the application consuming the OpenAPI document.\nSource: OAS 3.1 Specification\n\n\nIf you end up having to read through the specification to find out something which would fit better in a tutorial, please take the time to contribute that tutorial back to the JSON Schema website to avoid others needing to do the same.\n\nExtra OpenAPI-specific Properties\n\nAs mentioned above the OpenAPI Schema Object is a superset of JSON Schema Draft 2020-12, which means it supports everything and adds a few bits on top. This works because JSON Schema has the concept of dialects, and the OpenAPI Schema Object is a new dialect, which takes the JSON Schema vocabularies that give you all the keywords defined in the core and validation specifications, then adds four more:\n\n\n  discriminator - The discriminator is an object name that is used to differentiate between other schemas which may satisfy the payload description, acting as a shortcut for oneOf / anyOf that is no longer needed, but kept in OpenAPI v3.1 for compatibility with v3.0.\n  example - A free-form property to include an example of an instance for this schema. To represent examples that cannot be naturally represented in JSON or YAML, a string value can be used to contain the example with escaping where necessary. Deprecated: The example property has been deprecated in favor of the JSON Schema examples keyword. Learn more about examples.\n  externalDocs - Additional documentation found elsewhere outside of the OpenAPI or generated documentation, like tutorials or blog posts.\n  xml - Optional keyword for describing XML payloads, which does nothing on root schemas but helps describe properties where there may be wrapping tags or XML attributes.\n\n\nMost of this stuff can be ignored to build the majority of APIs especially if you’re JSON-only, but if you do use these keywords do not worry about losing compatibility with JSON Schema tooling. If JSON Schema spots keywords it does not understand, the default behavior is to ignore them completely."
        },
        {
          "id": "openapi-v3.1-data-models-schema-and-data-types",
          "title": "Schemas and Data Types",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/data-models/schema-and-data-types/",
          "content": "Data Formats\n  Validation    \n      const &amp; enum\n      default\n      minimum &amp; maximum\n      enum\n      pattern\n      required\n    \n  \n  readOnly &amp; writeOnly\n  Learn more about JSON Schema\n\n\nOne of the most important parts of OpenAPI is the schema object. Schema objects are used to describe HTTP request and response bodies, parameters, headers, and all sorts of other data, whether its JSON, XML, or primitive types like integers and strings.\n\n\n  If you’re familiar with JSON Schema, you’ll be right at home here, because OpenAPI v3.1 uses JSON Schema (draft 2020-12). For those who have not used JSON Schema before, that’s ok, follow along.\n\n\nThe first thing to learn about a schema is the type keyword, which can be one or more of the following types:\n\n\n  null\n  boolean\n  object\n  array\n  number\n  string\n\n\n\n  You can also use “integer” for the sake of convenience, but “integer” and “number” are basically identical because JSON itself does not make that distinction. Since there is no distinct JSON integer type, JSON Schema defines integers mathematically. This means that both 1 and 1.0 are equivalent, and are both considered to be integers.\n\n\nA schema object looks like this:\n\ntype: string\n\n\nIt can also define multiple types, for instance making a property nullable:\n\ntype: [string, null]\n\n\nIt also allows you to support properties that could be either a number or a numeric string.\n\ntype: [number, string]\n\n\nDescribing an object will often involve the use of properties to define what each of the objects properties should look like, and each of those properties will have a “subschema” that describes it.\n\ntype: object\nrequired:\n- name\nproperties:\n  name:\n    type: string\n  age:\n    type: integer\n\n\nArrays work in a similar way, with an items keyword allowing each item in the array to be described.\n\ntype: array\nitems:\n  type: string\n\n\nArrays of objects can be described by combining the two concepts:\n\ntype: array\nitems:\n  type: object\n  required:\n  - name\n  properties:\n    name:\n      type: string\n    age:\n      type: integer\n\n\nData Formats\n\nThe type keyword sets out the basic data type, but knowing something is a string or an integer is just the first step to understanding what that data is all about.\n\nFirst of all there’s the format keyword, which covers a predefined set of common formats:\n\ntype: array\nitems:\n  type: object\n  required:\n  - name\n  properties:\n    name:\n      type: string\n    email:\n      type: string\n      format: email\n    age:\n      type: integer\n      format: int32\n\n\nThe full list of formats defined in the JSON Schema Validation that OpenAPI v3.1 relies upon:\n\n\n  \n    date-time:  A string instance is valid against this attribute if it is a valid representation according to the “date-time” production as defined in RFC3339.\n  \n  \n    date:  A string instance is valid against this attribute if it is a valid representation according to the “full-date” production as defined in RFC3339.\n  \n  \n    time:  A string instance is valid against this attribute if it is a valid representation according to the “full-time” production as defined in RFC3339.\n  \n  \n    duration:  A string instance is valid against this attribute if it is a valid representation according to the “duration” production as defined in RFC3339.\n  \n  \n    email:  As defined by the “Mailbox” ABNF rule in RFC 5321, section 4.1.2 RFC5321.\n  \n  \n    idn-email:  As defined by the extended “Mailbox” ABNF rule in RFC 6531, section 3.3 RFC6531.\n  \n  \n    hostname:  As defined by RFC 1123, section 2.1 RFC1123, including host names produced using the Punycode algorithm specified in RFC 5891, section 4.4 RFC5891.\n  \n  \n    idn-hostname:  As defined by either RFC 1123 as for hostname, or an internationalized hostname as defined by RFC 5890, section 2.3.2.3 RFC5890.\n  \n  \n    ipv4:  An IPv4 address according to the “dotted-quad” ABNF syntax as defined in RFC 2673, section 3.2 RFC2673.\n  \n  \n    ipv6:  An IPv6 address as defined in RFC 4291, section 2.2 RFC4291.\n  \n  \n    uri:  A string instance is valid against this attribute if it is a valid URI, according to RFC3986.\n  \n  \n    uri-reference:  A string instance is valid against this attribute if it is a valid URI Reference (either a URI or a relative- reference), according to RFC3986.\n  \n  \n    iri:  A string instance is valid against this attribute if it is a valid IRI, according to RFC3987.\n  \n  \n    iri-reference:  A string instance is valid against this attribute if it is a valid IRI Reference (either an IRI or a relative- reference), according to RFC3987.\n  \n  \n    uuid:  A string instance is valid against this attribute if it is a valid string representation of a UUID, according to RFC4122.\n  \n  \n    uri-template: A string instance is valid against this attribute if it is a valid URI Template (of any level), according to RFC6570.\n  \n  \n    json-pointer:  A string instance is valid against this attribute if it is a valid JSON string representation of a JSON Pointer, according to RFC 6901, section 5 RFC6901.\n  \n  \n    relative-json-pointer: A string instance is valid against this attribute if it is a valid Relative JSON Pointer relative-json-pointer.\n  \n  \n    regex - A regular expression, which SHOULD be valid according to the ECMA-262 ecma262 regular expression dialect\n  \n\n\nYou can also define your own custom formats, which tooling will not understand, but that doesn’t matter as the specification tells tooling to ignore unknown formats.\n\nValidation\n\nIn addition to defining data types and formats, JSON Schema provides several validation keywords to enforce specific constraints on the data. Here are a few popular validation keywords:\n\nconst &amp; enum\n\nRestricting a value down to one or more potential values can be done with the const or enum keywords.\n\nFirst, a look at enum, as that keyword has been around longer and is more used:\n\ntype: string\nenum:\n  - pending\n  - fulfilled\n  - archived\n\n\nThis says the string can’t just be any old string, it has to be one of the approved values listed in enum.\n\n\n  Learn more about const on JSON-Schema.org: Enumerated Values.\n\n\nOpenAPI v3.1 gained the const keyword added in modern JSON Schema, which helps with describing something that can only ever be one value.\n\nThe JSON Schema tutorial uses the example of having a country field where you only support shipping to the United States for export reasons:\n\nproperties:\n  country:\n    const: United States of America\n\n\nThat’s one way to use it, but another is to act as a switch in a oneOf.\n\noneOf:\n  - title: Card\n    properties:\n      object:\n        type: string\n        const: card\n      number:\n        type: string\n      cvc:\n        type: integer\n      exp_month:\n        type: integer\n      exp_year:\n        type: integer\n  \n  - title: Bank Account\n    type: object\n    properties:\n      object:\n        const: bank_account\n        type: string\n      number:\n        type: string\n      sort_code:\n        type: string\n\n\nIn this example the object could be card or bank_account, but instead of defining that as an enum and the other properties all have to figure out whether they relate to cards or bank accounts, we use the const to help match the subschema.\n\n\n  Learn more about const on JSON-Schema.org: Constant Values, and read our guide on Schema Composition to learn more about oneOf.\n\n\ndefault\n\nSetting a default lets people and code know what to do when a value has not been provided.\n\ntype: string\ndefault: pending\nenum:\n  - pending\n  - fulfilled\n  - archived\n\n\nThis is useful for properties that have a common or expected value, allowing you to avoid having to specify it every time.\n\nminimum &amp; maximum\n\nThe minimum and maximum keywords allow you to specify the minimum and maximum values for numeric properties. For example:\n\ntype: number\nminimum: 0\nmaximum: 100\n\n\nThis schema ensures that the value of the property falls within the range of 0 to 100.\n\nenum\n\nThe enum keyword allows you to define a list of acceptable values for a property. For example:\n\ntype: string\nenum:\n  - apple\n  - banana\n  - orange\n\n\nThis schema restricts the property value to be one of the specified options: “apple”, “banana”, or “orange”.\n\npattern\n\nThe pattern keyword allows you to enforce a specific regular expression pattern for string properties. For example:\n\ntype: string\npattern: ^[A-Za-z]+$\n\n\nThis schema ensures that the property value consists of only alphabetic characters.\n\nrequired\n\nThe required keyword is used to specify the required properties within an object. For example:\n\ntype: object\nrequired:\n  - name\n  - age\n\n\nThis schema mandates that the properties “name” and “age” must be present in the object.\n\nFor more information on JSON Schema validation keywords, you can refer to the JSON Schema Validation documentation.\n\nreadOnly &amp; writeOnly\n\nJSON Schema provides readOnly and writeOnly boolean keywords, which are really helpful in the context of an API, because resources are usually available in two flavours: the representation of a resource in a request body, and the representation of the resource in a response body.\n\n\n  \n    readOnly: true indicates that a value should not or cannot be be modified, but can be seen (e.g., id, created_at).\n  \n  \n    writeOnly: true indicates that a value may be set, but will remain hidden (e.g., password, or PII like the cvc security code on a credit card).\n  \n\n\ntype: object\nproperties:\n  id:\n    type: string\n    readOnly: true\n  username:\n    type: string\n  date_of_birth:\n    type: string\n    format: date-time\n  password:\n    type: string\n    writeOnly: true\n  created_at:\n    type: string\n    format: date-time\n    readOnly: true\n\n\nBy using these, a single schema can serve both requests and responses. For example, a User schema can send password during creation (POST), but exclude it in the GET response, while fields like id and created_at are only returned. This approach reduces duplication, making schemas easier to maintain.\n\nThese two keywords are considered “annotations” in JSON Schema, which means they are only there for various bits of tooling to do something with if they like. There is no requirement for tools to do anything in particular, but a common convention for most documentation tools will be to skip listing a readOnly property for a HTTP POST/PATCH request body, and similarly skip documenting writeOnly properties in a response body.\n\nThis same approach extends to example request/responses of JSON data generated by documentation tools, and also mock servers, and even change the parameters available for generated SDK code.\n\n\n  There are some complexities here with HTTP PUT because you’re meant to be sending the entire resource each time whether something is writeable or not, but if a property was removed from a sample request for an HTTP PUT request, that means: a) “send it or not, we don’t care”, or b) not sending it will result in it being removed. Seeing as tooling varies on this, and API implementations vary in how they interpret missing values in PUT, you just need to check the tools you use do what your API expects.\n\n\nLearn more about JSON Schema\n\nThere is a lot more to JSON Schema and OpenAPI Schema Objects than we’ve covered here, but this will hopefully get you off to a good start. If you need to learn more, you can read our guide on JSON Schema in OpenAPI."
        },
        {
          "id": "openapi-v3.1-data-models-representing-xml",
          "title": "Representing XML",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/data-models/representing-xml/",
          "content": "The XML Object\n  Examples of the XML Object    \n      Changing the Element Name\n      Using Namespaces\n      Using Attributes\n    \n  \n\n\nOpenAPI is set up with the assumption that you’re most likely describing JSON, because that’s what over 80% of APIs are using, but XML is still in the game and you could be using both in the same API. OpenAPI supports this with the xml keyword, which helps when XML output is using XML-specific syntax like attributes and wrapped arrays.\n\nBy combining schema composition and references, it’s possible to create reusable components that be used for both JSON and XML output, like this simplistic example below.\n\nopenapi: 3.1.0\ninfo:\n  title: Representing XML\n  description: An API that supports advanced XML\n  version: 1.0.0\npaths:\n  /stations:\n    get:\n      description: Get a list of train stations\n      operationId: get-stations\n      responses:\n        '200':\n          description: OK\n          content:\n            application/xml:\n              schema:\n                $ref: '#/components/schemas/Stations'\ncomponents:\n  schemas:\n    Stations:\n      type: array\n      items:\n        $ref: \"#/components/schemas/Station\"\n\n    Station:\n      type: object\n      xml:\n        name: stations\n      properties:\n        id:\n          type: string\n          format: uuid\n          examples:\n          - b2e783e1-c824-4d63-b37a-d8d698862f1d\n        name:\n          type: string\n          description: The name of the station\n          examples:\n          - Paris Gare du Nord\n        address:\n          type: string\n          examples:\n          - 18 Rue de Dunkerque 75010 Paris, France\n        country_code:\n          type: string\n          format: iso-country-code\n          examples:\n          - FR\n\n\nThis OpenAPI would be used to describe XML data that looked like this:\n\n&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;Station xmlns=\"http://example.com/xml/namespace\"&gt;\n  &lt;id&gt;b2e783e1-c824-4d63-b37a-d8d698862f1d&lt;/id&gt;\n  &lt;name&gt;Paris Gare du Nord&lt;/name&gt;\n  &lt;address&gt;18 Rue de Dunkerque 75010 Paris, France&lt;/address&gt;\n  &lt;country_code&gt;FR&lt;/country_code&gt;\n&lt;/Station&gt;\n\n\nWhen using $ref and components like this, most tools will grab the tag name from the $ref, which is how we got Stations.\n\nIf you want to control that name, you can set the xml.name keyword on the schema, and change it.\n\ncomponents:\n  schemas:\n   Stations:\n      type: array\n      items:\n        $ref: \"#/components/schemas/Station\"\n\n    Station:\n      type: object\n      xml:\n        name: station\n      properties:\n        # ...\n\n\n&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;station&gt;\n\t&lt;id&gt;b2e783e1-c824-4d63-b37a-d8d698862f1d&lt;/id&gt;\n\t&lt;name&gt;Paris Gare du Nord&lt;/name&gt;\n\t&lt;address&gt;18 Rue de Dunkerque 75010 Paris, France&lt;/address&gt;\n\t&lt;country_code&gt;FR&lt;/country_code&gt;\n&lt;/station&gt;\n\n\nBetter! Lowercase looks better, now lets get it wrapped in a tag so we can have multiple using the xml.wrapped property.\n\ncomponents:\n  schemas:\n    Stations:\n      type: array\n      xml:\n        name: data\n        wrapped: true\n      items:\n        $ref: \"#/components/schemas/Station\"\n\n    Station:\n      type: object\n      xml:\n        name: station\n\n\nThat looks like this:\n\n&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;data&gt;\n\t&lt;station&gt;\n\t\t&lt;id&gt;b2e783e1-c824-4d63-b37a-d8d698862f1d&lt;/id&gt;\n\t\t&lt;name&gt;Paris Gare du Nord&lt;/name&gt;\n\t\t&lt;address&gt;18 Rue de Dunkerque 75010 Paris, France&lt;/address&gt;\n\t\t&lt;country_code&gt;FR&lt;/country_code&gt;\n\t&lt;/station&gt;\n&lt;/data&gt;\n\n\nNow the XML looks a bit more ready for action, but if that’s not enough to get your API and OpenAPI on the same page about your XML structure there are plenty of keywords to play with:\n\nThe XML Object\n\nInside the xml object you can use any of these keywords:\n\n\n  name: (string) Replaces the name of the element/attribute used for the described schema property. When defined within items, it will affect the name of the individual XML elements within the list. When defined alongside type being array (outside the items), it will affect the wrapping element and only if wrapped is true. If wrapped is false, it will be ignored.\n  namespace: (string) The URI of the namespace definition. This MUST be in the form of an absolute URI.\n  prefix: (string) The prefix to be used for the name.\n  attribute: (boolean) Declares whether the property definition translates to an attribute instead of an element. Default value is false.\n  wrapped: (boolean) MAY be used only for an array definition. Signifies whether the array is wrapped (for example, &lt;books&gt;&lt;book/&gt;&lt;book/&gt;&lt;/books&gt;) or unwrapped (&lt;book/&gt;&lt;book/&gt;). Default value is false. The definition takes effect only when defined alongside type being array (outside the items).\n\n\nExamples of the XML Object\n\nHere are a few examples of how the xml object can be used in OpenAPI.\n\nChanging the Element Name\n\nYou can use the name keyword to change the name of a single property too. for example changing the name of the id property in XML only.\n\ncomponents:\n  schemas:\n    Station:\n      type: object\n      properties:\n        id:\n          type: string\n          format: uuid\n          xml:\n            name: stationId\n        ...\n\n\nThis will result in the following XML:\n\n&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;station&gt;\n  &lt;stationId&gt;b2e783e1-c824-4d63-b37a-d8d698862f1d&lt;/stationId&gt;\n  ...\n&lt;/station&gt;\n\n\nUsing Namespaces\n\nIf you need to define a namespace for your XML elements, you can use the namespace keyword. Here’s an example:\n\ncomponents:\n  schemas:\n    Stations:\n      type: array\n      xml:\n        name: data\n        namespace: http://example.com/xml/namespace\n        wrapped: true\n        # ...\n\n\nThe resulting XML will include the namespace declaration:\n\n&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;data xmlns=\"http://example.com/xml/namespace\"&gt;\n\t&lt;station&gt;\n    ...\n\n\nUsing Attributes\n\nTo define an XML attribute instead of an element, you can use the attribute keyword. Here’s an example:\n\ncomponents:\n  schemas:\n    Stations:\n      type: array\n      xml:\n        name: data\n        wrapped: true\n      items:\n        $ref: \"#/components/schemas/Station\"\n\n    Station:\n      type: object\n      xml:\n        name: station\n      properties:\n        id:\n          type: string\n          format: uuid\n          xml:\n            attribute: true\n        # ...\n\n\nThis will result in the following XML, where the id is now an attribute:\n\n&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;data&gt;\n\t&lt;station id=\"b2e783e1-c824-4d63-b37a-d8d698862f1d\"&gt;\n\t\t&lt;name&gt;Paris Gare du Nord&lt;/name&gt;\n\t\t&lt;address&gt;18 Rue de Dunkerque 75010 Paris, France&lt;/address&gt;\n\t\t&lt;country_code&gt;FR&lt;/country_code&gt;\n\t&lt;/station&gt;\n&lt;/data&gt;\n\n\nThese are just a few examples of how you can use the xml object in OpenAPI to customize the representation of XML data. Explore all the options and see how it looks in various API documentation tools until you get the hang of it."
        },
        {
          "id": "openapi-v3.1-data-models-schema-composition",
          "title": "Schema Composition",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/data-models/schema-composition/",
          "content": "What are oneOf, anyOf, and allOf?    \n      oneOf\n      anyOf\n      allOf\n    \n  \n  References in Composition\n\n\nIn OpenAPI v3.1 and JSON Schema, you can use oneOf, allOf, and anyOf keywords to handle composition, which is the concept of combining multiple schemas and subschemas in various ways to handle polymorphism, or “extending” other schemas to add more criteria.\n\nWhat are oneOf, anyOf, and allOf?\n\n\n  allOf: (AND) Must be valid against all of the subschemas.\n  anyOf: (OR) Must be valid against any of the subschemas.\n  oneOf: (XOR) Must be valid against exactly one of the subschemas.\n\n\nAll of these keywords must be an array, where each item is a schema. Be careful with recursive schemas as they can exponentially increase processing times.\n\noneOf\n\nThe oneOf keyword is used when you want to specify that a value should match one of the given schemas exactly. It’s useful when you have different possible data structures or types for a particular field, like accepting bank account or card payments, or having train tickets and tram tickets, which are similar but a little different.\n\nThe validation will pass if the value matches exactly one of the schemas defined in oneOf.\n\nThis can be done for a single value:\n\nproperties:\n\ttimestamp:\n    oneOf:\n    - type: string\n      format: date-time\n      examples:\n      - '2024-07-21T17:32:28Z'\n    - type: integer\n      examples: \n      - 1721820298\n\n\nIn this example the timestamp property could be either a RFC 3339 date time (e.g. 2017-07-21T17:32:28Z) or a unix timestamp (e.g. 1721820298).\n\nThat shows how it works for a single property, but oneOf can also be used with whole objects:\n\n properties:\n    source:\n      oneOf:\n        - title: Card\n          properties:\n            number:\n              type: string\n            cvc:\n              type: integer\n            exp_month:\n              type: integer\n              format: int64\n            exp_year:\n              type: integer\n              format: int64\n          required:\n            - number\n            - cvc\n            - exp_month\n            - exp_year\n        \n        - title: Bank Account\n          type: object\n          properties:\n            number:\n              type: string\n            sort_code:\n              type: string\n            account_type:\n              type: string\n              enum:\n                - individual\n                - company\n          required:\n            - number\n            - account_type\n\n\n\nIn the above example, the source property will be an object either way, but the properties contained within can be in one of two combinations. Either number, cvc, exp_month, and exp_year are valid and in good form (a card payment), or  number, sort_code, and account_type will be sent (a bank account). These are the only two outcomes which will return a valid result in a validator, because if neither subscheme match it will be invalid, and if multiple subschemas match that will also be invalid.\n\nanyOf\n\nThe anyOf keyword is very similar to oneOf but a little less restrictive. oneOf is more like a XOR in programming, where one or the other can match, but never both. anyOf is more like a regular OR, which allows one or another or both.\n\nJust like oneOf, you can use anyOf when you have multiple valid options for a particular field. The validation will pass if the value matches one or more of the listed subschemas.\n\nanyOf:\n  - type: number\n    multipleOf: 5\n  - type: number\n    multipleOf: 3\n\n\nThe values 1, 2, 4, 7, 8, 11, 13, 14 would all be rejected for not being multiples of either 3 or 5.\n\nThe values 3, 5, 6, 9, 10, 12 would be valid for being multiples of 3 or 5.\n\nThe value 15 is valid because it is multiples of both 3 and 5, and anyOf is fine with that.\n\nallOf\n\nThe allOf keyword is used when you want to specify that a value should match all of the given schemas. It is useful when you want to combine multiple schemas together. The validation will pass if the value matches all of the schemas defined in allOf.\n\nallOf:\n  - type: object\n    properties:\n      name:\n        type: string\n  - type: object\n    properties:\n      age:\n        type: integer\n        minimum: 0\n\n\nIn the above example, the value should be an object that has both a name property of type string and an age property of type integer with a minimum value of 0.\n\nReferences in Composition\n\nAll of these keywords can contain a list of subschemas that are defined directly inside them, or a $ref can point to a schema defined elsewhere.\n\nschema:\n  oneOf:\n    - $ref: '#/components/schemas/Card'\n    - $ref: '#/components/schemas/BankAccount'\n\n\nThis says that the schema can be either one of these schemas stored as shared components.\n\nDue to the way allOf works, you can essentially reference multiple schemas and say “I want all of the validation rules and criteria from all of these schemas to apply here”, providing a sort of merge-like functionality.\n\nschema:\n  allOf:\n    - $ref: '#/components/schemas/PaymentMethod'\n    - $ref: '#/components/schemas/BankAccount'\n\n\nThis basically declares a schema which has a lot of generic payment fields, then adds specific fields from the bank account type, to avoid declaring generic fields like “name” and “number” in both.\n\nFeel free to mix and match a $ref and an inline subschema, which is a handy way to pop some extra content into a generic shared schema like HATEOAS links:\n\ncontent:\n  application/json:\n    schema:\n      allOf:\n        - $ref: '#/components/schemas/Booking'\n        - properties:\n            links:\n              $ref: '#/components/schemas/Links-Self'\n\n\nThese schema composition keywords provide flexibility and allow you to define complex data structures and validation rules in OpenAPI v3.1 and JSON Schema, which becomes more useful as you start to improve reuse across one or more API."
        },
        {
          "id": "openapi-v3.1",
          "title": "OpenAPI 3.1 Specification Complete Guide",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/",
          "content": "OpenAPI is the industry standard for describing HTTP APIs. Maintained by the OpenAPI Initiative under the Linux Foundation, it provides a structured, machine-readable format that tools can use to generate documentation, client libraries, tests, mock servers, and more.\n\nThis guide covers every aspect of the OpenAPI 3.1 specification, from the basics of document structure to advanced topics like security, webhooks, and overlays. A 3.2 version of this guide is also available.\n\nIntroduction\n\n\n  What is OpenAPI?: what the specification is, what an OpenAPI document looks like, and the key concepts (documents, operations, schemas, security).\n  History and evolution: from Swagger to OpenAPI 3.0 and 3.1 with its full JSON Schema alignment.\n  Benefits of using OpenAPI: how OpenAPI accelerates documentation, testing, SDK generation, onboarding, and collaboration across teams.\n\n\nUnderstanding OpenAPI structure\n\nThe core building blocks of every OpenAPI document.\n\n\n  Basic structure: the main sections of an OpenAPI document: version, info, servers, paths, components, security, webhooks, and tags.\n  API servers: defining base URLs for different environments with server variables.\n  Paths and operations: describing endpoints and HTTP methods with request bodies and responses.\n  Parameters: path, query, header, and cookie parameters with types and validation.\n  Parameter serialization: how arrays and objects are encoded in URLs using styles like form, spaceDelimited, and deepObject.\n  HTTP requests: defining request bodies with content types and schemas.\n  HTTP responses: documenting status codes, response headers, and content types.\n  Components: reusable schemas, parameters, responses, and security schemes referenced with $ref.\n\n\nDefining data models\n\n\n  Schemas and data types: types, formats, validation keywords (enum, minimum, maximum, pattern), and readOnly/writeOnly.\n  JSON Schema in OpenAPI: the relationship between OpenAPI 3.1 and JSON Schema Draft 2020-12.\n  Examples and defaults: providing sample values at schema, media type, and parameter levels.\n  Schema composition: combining schemas with allOf, anyOf, and oneOf for polymorphism and inheritance.\n  Representing XML: customizing XML element names, attributes, and namespaces.\n\n\nAdvanced topics\n\n\n  Multiple content types: serving JSON, XML, CSV, and other formats from a single endpoint.\n  Multipart form data: describing form submissions that combine text fields and files.\n  File uploads: direct binary uploads and multipart uploads with encoding.\n  Error formats: standardized error responses with RFC 9457 Problem Details and JSON:API.\n  Security: authentication schemes (API keys, HTTP bearer, OAuth2, OpenID Connect, mutual TLS) with scopes.\n  Callbacks and webhooks: describing asynchronous events sent by the API to clients.\n  Splitting documents with $ref: managing large API definitions across multiple files for better collaboration.\n\n\nDocumenting APIs\n\n\n  Descriptions and summaries: writing clear summaries and descriptions that improve developer experience.\n  Grouping with tags: organizing endpoints into logical sections with descriptions and ordering.\n  External documentation: linking to tutorials and guides from operations, tags, and schemas.\n\n\nExtending OpenAPI\n\n\n  Specification extensions: adding custom x-* properties for vendor-specific features.\n  Overlays: non-destructively modifying OpenAPI documents using JSONPath targeting.\n\n\nWorkflow and best practices\n\n\n  The perfect modern OpenAPI workflow: using OpenAPI as a single source of truth with automated linting, testing, documentation, and SDK generation.\n\n\nQuick reference\n\n\n  The Cheat Sheet: a downloadable one-page visual reference of the specification."
        },
        {
          "id": "openapi-v3.1-introduction-benefits",
          "title": "OpenAPI Benefits",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/introduction/benefits/",
          "content": "API Design-First\n  Code-first\n  Tools for OpenAPI    \n      Editors\n      Linters\n      Documentation\n      Mocking\n      Testing\n      Clients generator\n      Observability\n    \n  \n  Give it a try\n\n\nOpenAPI is a game-changer for saving your team time and money. By automating routine tasks like creating detailed API documentation, generating client libraries and generating chunks of server-side code, even automating the checking of the API against style guides, it frees up your developers to focus on more important work. This means faster development and fewer hours spent on repetitive coding, which saves time and money as better quality APIs can be delivered quicker.\n\nIt also makes collaboration smoother and more efficient. With OpenAPI, you have a clear, consistent description of your API that everyone can follow. This reduces misunderstandings and miscommunications between different teams—whether they’re front-end, back-end, or QA. Fewer mix-ups mean less time fixing errors and more time building great features.\n\nWhen it comes to testing and validation, OpenAPI shines by enabling automated testing against your API specifications. This catches bugs early in the development process, which is cheaper and easier to fix than issues found later on. Reliable, bug-free APIs lead to happy users and less downtime, saving you from costly fixes and lost customers.\n\nOnboarding new developers is made a lot more efficient with OpenAPI. The detailed documentation helps new team members or customers quickly understand how your APIs work, learning about key validation rules, cutting down on the “time to first request” by sharing sample HTTP requests in curl or code samples in various programming languages. Faster onboarding means new hires can start contributing sooner, and customers can start paying for use sooner.\n\nAPI Design-First\n\nIn an API design-first world, OpenAPI allows you to describe your whole API from endpoints to examples before even writing the first line of code.\n\nUsing this approach, OpenAPI becomes the cornerstone of your API, and becomes the single source of truth in your organization. Code is based on what has been validated during the design phase, and the documentation is generated and synced with the OpenAPI document by deploying updated docs when new commits are merged.\n\nYour team can collaborate at every step of the API design phase and leverage their workflow:\n\n\n  \n    Business and product teams can specify new features that meet consumers needs and a technical writer or an engineer can create or update the OpenAPI documents. Teams can discuss the changes, test the impacts and validate them.\n  \n  \n    The API design process is boosted: frontend and backend developers can use the OpenAPI file to start working on the implementation, even if this is not the final version of the document.\n  \n\n\nCode-first\n\nObviously, we can’t talk about API Design-First without mentioning the previous popular approach of Code-First, as it can have some benefits as well.\n\nIf you need to deploy an API fast for a MVP, internal use or with few endpoints, spending time on API design before you start coding may not be necessary and may slow your delivery time.\n\nAs developers, we have our rooted habits and Code-First follows the historical development process. We put ourselves directly into coding, without the need to learn yet another language or design tools to create our APIs. Sometimes it is a great time saver.\n\nTools for OpenAPI\n\nThere are many tools to help you get the most out of OpenAPI, at every step of the API life cycle, here is a selection of our preferred ones:\n\nEditors\n\n\n  OpenAPI-GUI\n  Stoplight Studio\n  Swagger Editor\n  Insomnia\n\n\nLinters\n\n\n  Spectral\n  Vacuum\n\n\nDocumentation\n\n\n  Bump.sh 💙\n  Swagger UI\n  Redoc\n  Readme\n\n\nMocking\n\n\n  Microcks\n  Prism\n  Wiretap\n\n\nTesting\n\n\n  Microcks\n  Postman\n\n\nClients generator\n\n\n  OpenAPI Generator\n\n\nObservability\n\n\n  Akita\n  Optic\n\n\nBesides the ones mentioned above, here is an amazing and more exhaustive list of curated tools for OpenAPI: https://openapi.tools/\n\nGive it a try\n\nNow that you know what OpenAPI is, try it out with one of the following OpenAPI documents.\n\n\n  Train Travel API\n  Bump API\n\n\nOr why not learn to make your own, heading over to the next section: Understanding the Structure of OpenAPI."
        },
        {
          "id": "openapi-v3.1-introduction-history",
          "title": "A brief history of OpenAPI",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/introduction/history/",
          "content": "Major differences between OpenAPI 2.0, 3.0, 3.1    \n      Versions\n      Structural changes\n    \n  \n\n\nThe OpenAPI Specification started off life with another name, and this can cause a bit of confusion. Until version 3.0, the specification was still called “Swagger”, before being renamed to “OpenAPI” in 2016. It’s actually a “retroactive” rename, so even v2.0 and earlier are called OpenAPI now.\n\nThe OpenAPI Specification is now supervised by the OpenAPI Initiative, an open-source project under the Linux Foundation.\n\nThe name Swagger is still popular, and many of the tools have the word Swagger in, but generally speaking you are better off searching for “OpenAPI tools” than “Swagger tools” because those are mostly old outdated tools which don’t work with modern versions of OpenAPI.\n\nMajor differences between OpenAPI 2.0, 3.0, 3.1\n\nVersions\n\nIn the 2.0 specification, a property called swagger indicated which version of the specification you are using. In OpenAPI 3.0, this is replaced by a new openapi property:\n\n\n  swagger: \"2.0\" line is thus transformed into openapi: \"3.0.0\"\n\n\nStructural changes\n\nThe following image sums up the main structural changes between 2.0 and 3.0. As you can see, a simplification effort has been made to group each concern in a more logical way.\n\n\n\nIf you want to get more into the details about what changed between OpenAPI 3.0 and 3.1, you can have a look at our dedicated article here."
        },
        {
          "id": "openapi-v3.1-introduction-what-is-openapi",
          "title": "What is OpenAPI?",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/introduction/what-is-openapi/",
          "content": "Concepts\n  OpenAPI structure\n  Format    \n      OpenAPI Specification\n      OpenAPI Document\n      OpenAPI Documentation\n    \n  \n\n\nOpenAPI (the “OpenAPI Specification”) is a standard for describing an API. OpenAPI is managed by the OpenAPI Initiative (OAI). The OpenAPI Specification (OAS) defines an open and independent description format for HTTP API services, which allows both humans and computers to discover and understand how an API works and how to interact with it, without the need to look at the source code.\n\nOpenAPI provides a machine-readable structured data format which can be also be read and written by people, allowing for tooling to help API developers, API product managers, technical writers, and governance teams, all the way through the API lifecycle.\n\n\n\nDiagram created by OpenAPI Initiative.\n\nConcepts\n\nAs with many things in computing, the terminology can be dense. Some definitions overlap, others vary, and it’s easy to get confused. Here’s a quick refresher to help clarify each concept and keep things straight.\n\nOpenAPI structure\n\nYour OpenAPI documents lets you describe your REST API:\n\n\n  Define general information about your API: description, terms of use, license, contact, etc…\n  Authentication methods HTTP, API keys, OAuth 2, OpenID, etc…\n  Available endpoints /users, etc…\n  Since OpenAPI 3.1, available webhooks\n  Available operations on each endpoint: GET, POST, PUT, PATCH, DELETE, etc…\n  Input and output parameters for each operation\n\n\nFormat\n\nOpenAPI documents can be written in YAML or JSON formats.\n\nThese formats were chosen because they are easy for a human to read and write, and easy for machines to parse. In practice, YAML is the most used format adopted to write OpenAPI documents. Like it or not, YAML is easier to read than JSON mainly because it reduces the use of markup tags. Also, it is a format that is widely used to write any sort of software configuration.\n\nHere is an example of a partial OpenAPI document covering one endpoint, written in YAML:\n\n/previews:\n  post:\n    summary: Create a preview\n    description: |\n      Create a preview for a given documentation file. The preview will have a unique\n      temporary URL, and will be active for 30 minutes.\n    security: []\n    requestBody:\n      $ref: \"#/components/requestBodies/Preview\"\n    responses:\n      \"201\":\n        description: \"Success\"\n        content:\n          \"application/json\":\n            schema:\n              $ref: \"#/components/schemas/Preview\"\n\n\nOpenAPI Specification\n\nAlso known as OpenAPI spec / OAS, the “OpenAPI Specification” refers to the official specification developed and maintained by the OpenAPI Initiative, and published at spec.openapis.org. It’s a technical standard designed to ensure consistency for OpenAPI users and tooling providers, defining how things are expected to work.\n\nOpenAPI Document\n\nAlso known as OpenAPI documents / OpenAPI file / OpenAPI description / OpenAPI contract, an OpenAPI document outlines how your API behaves—or how it’s intended to behave once implemented—and is written according to the OpenAPI Specification. It serves as the blueprint for your API. While “API description” can be a broad term, an OpenAPI Document is specific: it’s the actual file, usually named openapi.yaml or openapi.json, that defines your API.\n\nOpenAPI Documentation\n\nAlso known as API Reference, one of the most common uses of an OpenAPI document is to generate API documentation—specifically, “API Reference Documentation”. This is human-readable technical content that presents all the key details about your API’s endpoints, parameters, requests, and responses. It can be automatically generated from your OpenAPI document, saving you from manually writing it all out."
        },
        {
          "id": "openapi-v3.1-advanced-multipart-form-data",
          "title": "Multipart Form Data",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/advanced/multipart-form-data/",
          "content": "Content Encoding\n  Content Media Type\n  Encoding Object\n\n\nMultipart form data is a content type used for HTTP requests to send a combination of text and file data in a single request, allowing for one or more uploaded files to be sent with other form fields.\n\nIn the context of an API, using multipart form data enables clients to send files to the server as part of a single API request, avoiding the need for metadata to be uploaded as one request then linking it back to the file somehow. This is handy when building resources that might have an avatar or a cover image, or sending a PDF that needs a name, description, and list of recipients.\n\nPOST /upload HTTP/2\nHost: api.example.com\nContent-Type: multipart/form-data; boundary=--------------------------1234567890\n\n----------------------------1234567890\nContent-Disposition: form-data; name=\"metadata\"\nContent-Type: application/json\n\n{\n  \"name\": \"My PDF Document\",\n  \"recipients\": [\n    \"andy@example.com\",\n    \"sarah@example.com\"\n  ]\n}\n----------------------------1234567890\nContent-Disposition: form-data; name=\"description\"\n\nThis is a long form text field which can also be sent if JSON is not your thing.\n----------------------------1234567890\nContent-Disposition: form-data; name=\"file\"; filename=\"document.pdf\"\nContent-Type: application/octet-stream\n\n[base64 encoded PDF file data]\n----------------------------1234567890--\n\n\nOpenAPI supports APIs using multipart form data in requestBody, by specifying the multipart/form-data content type.\n\npaths:\n  /upload:\n    post:\n      summary: Upload a file\n      description: Endpoint to upload a file\n      requestBody:\n        required: true\n        content:\n          multipart/form-data:\n            schema:\n              # ...\n                \n\n\nWithin the schema property of the requestBody, you must define the structure of the multipart form data. When boundaries are used to separate segments of the content being transferred, you will need to describe the Content-Types, or you can leave it with the sensible default values:\n\n\n  If the property is a primitive, or an array of primitive values, the default Content-Type is text/plain.\n  If the property is complex, or an array of complex values, the default Content-Type is application/json.\n  If the property is a type: string with a contentEncoding, the default Content-Type is application/octet-stream.\n\n\n  requestBody:\n    required: true\n    content:\n      multipart/form-data:\n        schema:\n          type: object\n          properties:\n            # default Content-Type for objects is `application/json`\n            metadata: \n              type: object\n              properties: \n                name: \n                  type: string\n                recipients:\n                  type: array\n                  items: \n                    type: string\n            # Content-Type for application is `text/plain`\n            description:\n              type: string\n            # Content-Type for a string with a contentEncoding set is `application/octet-stream`\n            file:\n              type: string\n              contentEncoding: base64\n\n\nThis OpenAPI declares the schema to match the previous HTTP request example, documenting a JSON object with name and recipient array, a plain text description. No content media types had to be defined for either of those segments because OpenAPI will guess them, and usually guess right. The object is assumed to be JSON. The string with no other information is assumed to be plain text.\n\nOther sorts of segments can be a bit tricker, and need a bit of configuration.\n\nContent Encoding\n\nBy default if a string has a contentEncoding set, the default will be application/octet-stream, which will accept any sort of file. This API would like a PDF but could also accept images and other files, so that’s often just fine, and the server can reject invalid files later on.\n\nThe contentEncoding is there to help define what form the data is in. Some file uploads are base64 encoded, so that can be described with contentEncoding: base64. The contentEncoding keyword supports all encodings defined in RFC4648, including \"base64\" and \"base64url\", as well as \"quoted-printable\" from RFC2045 Section 6.7.\n\nContent Media Type\n\nWhen you need to override the default contentMediaType assumptions for any of the multipart segments, you can update the schema for that segment to be as specific as you want.\n\n  content:\n    multipart/form-data:\n      schema:\n        type: object\n        properties:\n          # One specific file type, but still text\n          csvSpecifically:\n            type: string\n            contentMediaType: text/csv\n          # Either PNG or JPEG, both encoded as base64\n          pngOrJpegImage:\n            type: string\n            contentMediaType: image/png, image/jpeg\n            contentEncoding: base64\n          # Any image is fine, but it'll be base64\n          anyImage:\n            type: string\n            contentMediaType: image/*\n            contentEncoding: base64\n\n\nYou can use a single mime type, a comma separated list, or use a wildcard (*) to support multiple.\n\nEncoding Object\n\nThe contentEncoding and contentMediaType keywords should handle most scenarios, but if you need to get even more involved with describing each of the multipart segments, including even the headers on that segment, take a look at the encoding object.\n\nrequestBody:\n  content:\n    multipart/form-data:\n      schema:\n        type: object\n        properties:\n          id:\n            # default is text/plain\n            type: string\n            format: uuid\n          address:\n            # default is application/json\n            type: object\n            properties: {}\n          historyMetadata:\n            # need to declare XML format!\n            description: metadata in XML format\n            type: object\n            properties: {}\n          profileImage: {}\n      encoding:\n        historyMetadata:\n          # require XML Content-Type in utf-8 encoding\n          contentType: application/xml; charset=utf-8\n        profileImage:\n          # only accept png/jpeg\n          contentType: image/png, image/jpeg\n          headers:\n            X-Rate-Limit-Limit:\n              description: The number of allowed requests in the current period\n              schema:\n                type: integer\n\n\nTake a look at the OpenAPI v3.1 Specification to learn more about the Encoding Object, and see how to handle custom headers and even “styles” and “explode” to handle complex data expressed in a string form.\n\n\n  Multipart form data is only one way to handle file uploads, so read the File Uploads guide to see other ways to do it."
        },
        {
          "id": "openapi-v3.2-advanced-multipart-form-data",
          "title": "Multipart Form Data",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/advanced/multipart-form-data/",
          "content": "Content Encoding\n  Content Media Type\n  Encoding Object\n\n\nMultipart form data is a content type used for HTTP requests to send a combination of text and file data in a single request, allowing for one or more uploaded files to be sent with other form fields.\n\nIn the context of an API, using multipart form data enables clients to send files to the server as part of a single API request, avoiding the need for metadata to be uploaded as one request then linking it back to the file somehow. This is handy when building resources that might have an avatar or a cover image, or sending a PDF that needs a name, description, and list of recipients.\n\nPOST /upload HTTP/2\nHost: api.example.com\nContent-Type: multipart/form-data; boundary=--------------------------1234567890\n\n----------------------------1234567890\nContent-Disposition: form-data; name=\"metadata\"\nContent-Type: application/json\n\n{\n  \"name\": \"My PDF Document\",\n  \"recipients\": [\n    \"andy@example.com\",\n    \"sarah@example.com\"\n  ]\n}\n----------------------------1234567890\nContent-Disposition: form-data; name=\"description\"\n\nThis is a long form text field which can also be sent if JSON is not your thing.\n----------------------------1234567890\nContent-Disposition: form-data; name=\"file\"; filename=\"document.pdf\"\nContent-Type: application/octet-stream\n\n[base64 encoded PDF file data]\n----------------------------1234567890--\n\n\nOpenAPI supports APIs using multipart form data in requestBody, by specifying the multipart/form-data content type.\n\npaths:\n  /upload:\n    post:\n      summary: Upload a file\n      description: Endpoint to upload a file\n      requestBody:\n        required: true\n        content:\n          multipart/form-data:\n            schema:\n              # ...\n                \n\n\nWithin the schema property of the requestBody, you must define the structure of the multipart form data. When boundaries are used to separate segments of the content being transferred, you will need to describe the Content-Types, or you can leave it with the sensible default values:\n\n\n  If the property is a primitive, or an array of primitive values, the default Content-Type is text/plain.\n  If the property is complex, or an array of complex values, the default Content-Type is application/json.\n  If the property is a type: string with a contentEncoding, the default Content-Type is application/octet-stream.\n\n\n  requestBody:\n    required: true\n    content:\n      multipart/form-data:\n        schema:\n          type: object\n          properties:\n            # default Content-Type for objects is `application/json`\n            metadata: \n              type: object\n              properties: \n                name: \n                  type: string\n                recipients:\n                  type: array\n                  items: \n                    type: string\n            # Content-Type for application is `text/plain`\n            description:\n              type: string\n            # Content-Type for a string with a contentEncoding set is `application/octet-stream`\n            file:\n              type: string\n              contentEncoding: base64\n\n\nThis OpenAPI declares the schema to match the previous HTTP request example, documenting a JSON object with name and recipient array, a plain text description. No content media types had to be defined for either of those segments because OpenAPI will guess them, and usually guess right. The object is assumed to be JSON. The string with no other information is assumed to be plain text.\n\nOther sorts of segments can be a bit tricker, and need a bit of configuration.\n\nContent Encoding\n\nBy default if a string has a contentEncoding set, the default will be application/octet-stream, which will accept any sort of file. This API would like a PDF but could also accept images and other files, so that’s often just fine, and the server can reject invalid files later on.\n\nThe contentEncoding is there to help define what form the data is in. Some file uploads are base64 encoded, so that can be described with contentEncoding: base64. The contentEncoding keyword supports all encodings defined in RFC4648, including \"base64\" and \"base64url\", as well as \"quoted-printable\" from RFC2045 Section 6.7.\n\nContent Media Type\n\nWhen you need to override the default contentMediaType assumptions for any of the multipart segments, you can update the schema for that segment to be as specific as you want.\n\n  content:\n    multipart/form-data:\n      schema:\n        type: object\n        properties:\n          # One specific file type, but still text\n          csvSpecifically:\n            type: string\n            contentMediaType: text/csv\n          # Either PNG or JPEG, both encoded as base64\n          pngOrJpegImage:\n            type: string\n            contentMediaType: image/png, image/jpeg\n            contentEncoding: base64\n          # Any image is fine, but it'll be base64\n          anyImage:\n            type: string\n            contentMediaType: image/*\n            contentEncoding: base64\n\n\nYou can use a single mime type, a comma separated list, or use a wildcard (*) to support multiple.\n\nEncoding Object\n\nThe contentEncoding and contentMediaType keywords should handle most scenarios, but if you need to get even more involved with describing each of the multipart segments, including even the headers on that segment, take a look at the encoding object.\n\nrequestBody:\n  content:\n    multipart/form-data:\n      schema:\n        type: object\n        properties:\n          id:\n            # default is text/plain\n            type: string\n            format: uuid\n          address:\n            # default is application/json\n            type: object\n            properties: {}\n          historyMetadata:\n            # need to declare XML format!\n            description: metadata in XML format\n            type: object\n            properties: {}\n          profileImage: {}\n      encoding:\n        historyMetadata:\n          # require XML Content-Type in utf-8 encoding\n          contentType: application/xml; charset=utf-8\n        profileImage:\n          # only accept png/jpeg\n          contentType: image/png, image/jpeg\n          headers:\n            X-Rate-Limit-Limit:\n              description: The number of allowed requests in the current period\n              schema:\n                type: integer\n\n\nTake a look at the OpenAPI v3.1 Specification to learn more about the Encoding Object, and see how to handle custom headers and even “styles” and “explode” to handle complex data expressed in a string form.\n\n\n  Multipart form data is only one way to handle file uploads, so read the File Uploads guide to see other ways to do it."
        },
        {
          "id": "openapi-v3.1-advanced-callbacks-webhooks",
          "title": "Callbacks and Webhooks",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/advanced/callbacks-webhooks/",
          "content": "Backstory\n  Callbacks\n  Webhooks\n  Consider AsyncAPI\n  Examples\n\n\nREST/HTTP APIs are often considered to be a simple request and response model, but they have always been a lot more asynchronous than that, and OpenAPI can capture two types of asynchronous HTTP event using callbacks and webhooks.\n\nBoth of these types of events are basically a HTTP request being made by your API, which is the opposite of all the other requests and responses being defined, but it works in a fairly familiar way.\n\nBackstory\n\nIn traditional request/response API designs, the client makes a request and the server responds. This works well for many use cases, but sometimes you need the server to notify the client about certain events asynchronously. This is where callbacks and webhooks come into play:\n\n\n  callbacks Callbacks allow an API to send real-time data back to the client as soon as the event has completed, and without the client having to check for progress. This is often used in tandem with 202 Accepted responses which defer the actually processing to a queue, promising to let you know when the work is complete.\n  webhooks Webhooks are similar to callbacks, but might not be happening in response to any one web request being made. It could be a generic “updates” integration point between multiple web services, with all sorts of events being transmitted as different happen throughout the system.\n\n\nIn OpenAPI v3.0 you only had callbacks, but OpenAPI v3.1 added support for webhooks too.\n\nCallbacks\n\nLet’s start by setting up a callback in OpenAPI. Suppose we have an API for creating orders, and we want to notify the client when the order status changes.\n\npaths:\n  /orders:\n    post:\n      summary: Create a new order\n      operationId: createOrder\n      requestBody:\n        description: Order object that needs to be added\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              properties:\n                id:\n                  type: string\n                item:\n                  type: string\n                quantity:\n                  type: integer\n                callbackUrl:\n                  type: string\n                  format: uri\n      responses:\n        '201':\n          description: Order created\n          content:\n            application/json:\n              schema:\n                type: object\n                properties:\n                  id:\n                    type: string\n                  status:\n                    type: string\n      callbacks:\n        onOrderStatusChange:\n          '{$request.body#/callbackUrl}':\n            post:\n              summary: Callback for order status change\n              requestBody:\n                description: Status update payload\n                required: true\n                content:\n                  application/json:\n                    schema:\n                      type: object\n                      properties:\n                        orderId:\n                          type: string\n                        status:\n                          type: string\n              responses:\n                '200':\n                  description: Callback processed successfully\n\n\nThis could be broken into two parts: creating a path for registering for the callbacks, in this case creating an order, then the definition of that callbacks themselves.\n\nCallbacks need a name, which is mainly used within OpenAPI and any tools which choose to find a use for it. This one is called onOrderStatusChange but you can use any naming convention you like.\n\nThe next bit is '{$request.body#/callbackUrl}' which is a runtime expression, that looks for a callbackUrl property in the HTTP request body.\n\nWhen the client sends a request to the API they need to let the API know the URL to send the callback to.\n\nPOST /orders HTTP/2\nHost: api.example.com\nContent-Type: application/json\n\n{\n  \"id\": \"12345\",\n  \"item\": \"Widget\",\n  \"quantity\": 1,\n  \"callbackUrl\": \"https://example.com/callbacks/order-status-updates\"\n}\n\n\nThe callbackURL could be in headers or could be composed of various other bits of data in the request, take a look at OpenAPI v3.1’s runtime expressions syntax to get more advanced.\n\nOnce you’ve got a callback named and figured out where the URL is going, the rest of the OpenAPI is going to feel very familiar, only it’s backwards.\n\nYou describe the request that your API is going to send, which is letting the client know what the HTTP request you’re sending will look like.\n\nThen you describe a response that your API is hoping to see their API return. You can get really specific with this or just specify that a 200 or 2XX is needed to let your API know the callback request is received properly. Most callback implementers will choose to retry these callbacks at a later date if the wrong status code appears, or if other success criteria fail errors can be sent to the authenticated users contact details.\n\nWebhooks\n\nTechnically callbacks are actually HTTP Webhooks in implementation, but in OpenAPI a “webhook” is specifically something named under the webhooks root of your OpenAPI document.\n\nThey are defined in a similar way to paths, but with a name instead of a URL.\n\nopenapi: 3.1.0\ninfo:\n  title: Train Travel API\n  version: 1.0.0\nwebhooks:\n  newBooking:\n    post:\n      summary: New Booking\n      description: Subscribe to new bookings being created, to update integrations for your users.\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/Booking'\n      responses:\n        '200':\n          description: Return a 200 status to indicate that the data was received successfully.\n\n\n\n  This example has trimmed the components section out for brevity, but you can imagine any schema you like going in there.\n\n\nBoth webhooks and paths are lists of Path Item Objects, similarly to callbacks, they are all just HTTP requests, some coming in (paths to be called), some going out (webhooks to be sent).\n\nThe key bits to notice here are:\n\n\n  newBooking is a unique nickname\n  Summary is a short title\n  Description is longer form documentation with Markdown covering context like “why” and edge cases to think of not covered elsewhere.\n  The requestBody is the request you’re going to send.\n  The responses property is a map of responses you want the client to return.\n\n\nThese then don’t need to be tied to paths. In fact, you can have an OpenAPI document which has no paths. This could be because you’re splitting your documents up in interesting reusable ways with these webhooks being shared across multiple APIs, or because your API is entirely asynchronous.\n\nConsider AsyncAPI\n\nIf you are using a few webhooks in an API which is largely otherwise driven by request/response, OpenAPI will be just fine for you with callbacks and webhooks helping as needed.\n\nHowever if you are building an API that is entirely asynchronous, you are using technologies like WebSockets, or are working with any other event-driven API protocols, you should consider using AsyncAPI for describing those parts of the architecture.\n\nExamples\n\nThere’s a full example in either Train Travel API, or Bump.sh API: every structural change on API documentation generates a new request to this webhook.\n\nYou can also find more information about documenting webhooks in this blog post."
        },
        {
          "id": "openapi-v3.1-advanced-error-formats",
          "title": "Handling Error Formats",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/advanced/error-formats/",
          "content": "API Errors with JSON Responses\n  Other Error Formats    \n      RFC 9457: Problem Details\n      JSON:API Errors\n    \n  \n\n\nWhen your API is happy you return the data or whatever response the action would like to provide, but what happens when you stray off that happy path and into the world of errors?\n\nIn HTTP an error is any response returning a 400-599 status code, but that alone is generally not enough to tell an API client what to do, why it happened, what can be done to avoid it, when to try again, or anything else.\n\nHTTP API errors usually involve some sort of JSON payload which explains a few key things:\n\n\n  \n    A human readable short summary: “Cannot checkout with an empty shopping cart”\n  \n  \n    A human readable message: “It looks like you have tried to check out but there is nothing in your…”\n  \n  \n    An application-specific error code relating to the problem: ERRCARTEMPTY\n  \n  \n    Links to a documentation page or knowledge base where a client or user of the client can figure out what to do next.\n  \n\n\nAPI Errors with JSON Responses\n\nThe most basic API error might look a bit like this, with something for the humans to read, a specific error code for a machine to read that should describe a more specific situation to them (perhaps its documented somewhere), and/or a unique link to a specific error which can help a machine recognize the exact application-level problem from a predefined list.\n\nHTTP/1.1 400 Bad Request\nContent-Type: application/json\n\n{\n  \"error\": {\n    \"message\": \"Message describing the error\",\n    \"code\": \"ERR-01234\",\n    \"href\": \"http://example.org/docs/errors/#ERR-01234\"\n  }\n}\n\n\nThis JSON structure is not perfect but it’s realistic, so lets show how to describe it.\n\npaths:\n  /bookings:\n    get:\n      responses:\n        '200':\n          description: OK\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Booking'\n        '400':\n          $ref: '#/components/responses/BadRequest'\n        '401':\n          $ref: '#/components/responses/Unauthorized'\n        '404':\n          $ref: '#/components/responses/NotFound'\n        '409':\n          $ref: '#/components/responses/Conflict'\n        '429':\n          $ref: '#/components/responses/TooManyRequests'\n        '500':\n          $ref: '#/components/responses/InternalServerError'\n\n\nThis operation shows a success status of 201, and a whole variety of interesting potential errors that can be returned. Any HTTP interaction could have a 500 so maybe you don’t need to mention that, and there could be infinite other errors happening between your server and the client that would be impossible to guess, but if you aim to define as many errors for any given operation as seem relevant, you aren’t going to be far off.\n\nTo avoid repetition and ensure consistency, this example defines reusable HTTP error responses in the components section. These reusable components can then be referenced in multiple operations.\n\ncomponents:\n  schema:\n    Problem:\n      properties:\n        message:\n          type: string\n          description: An explanation of the problem.\n          example: Not enough credits in your account balance.\n        code:\n          type: string\n          description: An error code relating to an application-specific error scenario\n          example: ERR-01234\n        href:\n          type: string\n          format: url\n          description: A URL to find some more documentation on this problem if needed.\n          example: http://example.org/docs/errors/#ERR-01234\n\n  responses:\n    BadRequest:\n      description: Bad Request\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n          example:\n            message: The request is invalid or missing required parameters.\n            code: ERR-NAUGHTY001\n            href: https://example.com/docs/errors/ERR-NAUGHTY001\n\n    Conflict:\n      description: Conflict\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n          example:\n            message: There is a conflict with an existing resource.\n            code: ERR-EYYWOAH001\n            href: https://example.com/docs/errors/ERR-EYYWOAH001\n\n    Forbidden:\n      description: Forbidden\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n          example:\n            message: Access is forbidden with the provided credentials.\n            code: ERR-NAH001\n            href: https://example.com/docs/errors/ERR-NAH001\n\n    InternalServerError:\n      description: Internal Server Error\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n          example:\n            message: An unexpected error occurred.\n            code: ERR-OOF001\n            href: https://example.com/docs/errors/ERR-OOF001\n\n\nThis approach is one of many you could take, but essentially what we’re doing here is using one generic schema, then providing a tailored example for each type of error, with the types of error corresponding so far to the HTTP status codes likely to be returned.\n\nYou could get more specific and get into application-level errors here, but they might be better off left as examples and the specific errors all defined elsewhere, the same place that is being linked to with these href values.\n\nOther Error Formats\n\nAPI errors can be designed in a bunch of ways, and described in a bunch of ways, but as with most things in APIs: there’s a standard for that! Actually… there’s a few.\n\nRFC 9457: Problem Details\n\nRFC 9457: Problem Details for HTTP APIs (replacing very similar RFC 7807) defines loads of useful ideas and structure for API error messages. If we want to describe this, we can expand the main problem object from the last example.\n\ncomponents:\n  schema:\n    Problem:\n      properties:\n        type:\n          type: string\n          description: A URI reference that identifies the problem type\n          example: https://example.com/probs/out-of-credit\n        title:\n          type: string\n          description: A short, human-readable summary of the problem type\n          example: You do not have enough credit.\n        detail:\n          type: string\n          description: A human-readable explanation specific to this occurrence of the problem\n          example: Your current balance is 30, but that costs 50.\n        instance:\n          type: string\n          description: A URI reference that identifies the specific occurrence of the problem\n          example: /account/12345/msgs/abc\n        status:\n          type: integer\n          description: The HTTP status code\n          example: 400\n\n\nThen as before you can expand on that schema with better examples.\n\n  responses:\n    BadRequest:\n      description: Bad Request\n      content:\n        application/problem+json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n          example:\n            type: https://example.com/errors/bad-request\n            title: Bad Request\n            status: 400\n            detail: The request is invalid or missing required parameters.\n        \n    Conflict:\n      description: Conflict\n      content:\n        application/problem+json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n          example:\n            type: https://example.com/errors/conflict\n            title: Conflict\n            status: 409\n            detail: There is a conflict with an existing resource.\n     \n    Forbidden:\n      description: Forbidden\n      content:\n        application/problem+json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n          example:\n            type: https://example.com/errors/forbidden\n            title: Forbidden\n            status: 403\n            detail: Access is forbidden with the provided credentials.\n     \n    InternalServerError:\n      description: Internal Server Error\n      content:\n        application/problem+json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n          example:\n            type: https://example.com/errors/internal-server-error\n            title: Internal Server Error\n            status: 500\n            detail: An unexpected error occurred.\n\n\nHere the application/problem+json content type lets everyone know this is specifically using that RFC, the URIs replace both the status code and the link to documentation, and a title + detail allow room for a generic error message and instance specific details about the exact problem happening right now.\n\nJSON:API Errors\n\nAnother popular format for API errors is the JSON:API errors format. This format provides a standardized structure for representing errors in JSON responses.\n\nThe JSON:API errors format includes the following key components:\n\n\n  An array of error objects, each representing a specific error.\n  Each error object contains the following properties:\n    \n      id: A unique identifier for the error.\n      status: The HTTP status code associated with the error.\n      code: An application-specific error code.\n      title: A short, human-readable summary of the error.\n      detail: A detailed description of the error.\n      source: Information about the source of the error, such as the field or parameter that caused the error.\n    \n  \n\n\nThis is a little different as its an array of errors, but here’s how we can handle that with OpenAPI:\n\ncomponents:\n  schema:\n    Problem:\n      type: object\n      properties:\n        errors:\n          type: array\n          items:\n            type: object\n            properties:\n              id:\n                type: string\n                format: uuid\n                description: A unique identifier for the error.\n                examples: [\"2fe04316-9775-46af-987b-a12d8620d42e\"]\n              status:\n                type: string\n                description: The HTTP status code associated with the error.\n                examples: [\"404\"]\n              code:\n                type: string\n                description: An application-specific error code.\n                examples: [ERR-NOT-FOUND]\n              title:\n                type: string\n                description: A short, human-readable summary of the error.\n                examples: [Resource Not Found]\n              detail:\n                type: string\n                description: A detailed description of the error.\n                examples: [The requested resource could not be found.]\n              source:\n                type: object\n                properties:\n                  pointer:\n                    type: string\n                    description: Information about the source of the error, such as the field or parameter that caused the error.\n                    examples: [\"/data/id\"]\n                  parameter:\n                    type: string\n                    description: Information about the source of the error, such as the field or parameter that caused the error.\n                    examples: [id]\n\n\nThe fact that the schema is an array does not change the previous approach for describing errors, we just move that into the example:\n\n  responses:\n    BadRequest:\n      description: Bad Request\n      content:\n        application/vnd.api+json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n          example:\n            errors:\n              - id: \"2fe04316-9775-46af-987b-a12d8620d42e\"\n                status: \"400\"\n                code: \"ERR-BAD-REQUEST\"\n                title: \"Bad Request\"\n                detail: \"The request contained an id with an integer but its meant to be a UUID\"\n                source:\n                  pointer: \"/data/id\"\n                  parameter: \"id\"\n            \n    Conflict:\n      description: Conflict\n      content:\n        application/vnd.api+json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n          example:\n            errors:\n              - id: \"2fe04316-9775-46af-987b-a12d8620d42e\"\n                status: \"409\"\n                code: \"ERR-CONFLICT\"\n                title: \"Bad Request\"\n                detail: &gt; \n                  There is a conflict with an existing resource, the approved status\n                  cannot be changed back to pending.\n                source:\n                  pointer: \"/data/status\"\n                  parameter: \"status\"\n     \n    Forbidden:\n      description: Forbidden\n      content:\n        application/vnd.api+json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n          example:\n            errors:\n              - id: \"2fe04316-9775-46af-987b-a12d8620d42e\"\n                status: \"403\"\n                code: \"ERR-FORBIDDEN\"\n                title: \"Access is forbidden with the provided credentials.\"\n                detail: &gt; \n                  This action cannot be undertaken for this particular reason.\n                source:\n                  pointer: \"/header/Authorization\"\n                  parameter: \"Authorization\"\n\n\nIf you need to handle errors responses in multiple content types, you can learn about Multiple Content Types and it’s all the same."
        },
        {
          "id": "openapi-v3.1-advanced-file-uploads",
          "title": "Uploading a File",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/advanced/file-uploads/",
          "content": "APIs can handle file uploads in a variety of ways, and OpenAPI can help you describe any of them. The two most common methods are directly accepting the file based on the content-type of the request, e.g.: a HTTP request with image/png, image/jpeg, text/csv, etc., or a HTTP request with a multipart/form-data content type which allows you to combine text and file data in a single request.\n\nHere’s an example of accepting a CSV file being directly passed in the request body, showing how the HTTP request would look, then how that would be described in OpenAPI v3.1.\n\nGET /upload HTTP/1.1\nHost: api.example.org\nContent-Type: text/csv\n\nPlanted On,Longitude,Latitude,Unit Type,Species\n4/1/23,-3.88628,56.17454,tree,Aspen\n4/1/23,-3.8863,56.17455,tree,Silver Birch\n\n\npaths:\n  /upload:\n    post:\n      summary: Upload a CSV file\n      requestBody:\n        required: true\n        content:\n          text/csv: {}\n\n\nThe text/csv does not need to declare a schema if its transferred in a binary (octet-stream), as that is the default. The same goes for images.\n\ncontent:\n    image/png:\n        schema:\n            type: string\n            contentMediaType: image/png\n            contentEncoding: base64\n\n\npaths:\n  /upload:\n    post:\n      summary: Upload a file\n      description: Endpoint to upload a file\n      requestBody:\n        required: true\n        content:\n          multipart/form-data:\n            schema:\n              type: object\n              properties:\n                file:\n                  type: string\n                  contentMediaType: text/csv\n\n\nIn this example, the requestBody object specifies that the request body is required and should be in the multipart/form-data format. The schema defines an object with a single property file, which represents the uploaded file. The type is set to string and the format is set to binary to indicate that it is a binary file.\n\nTo upload images and a CSV file using the contentEncoding keyword, you can modify the schema as follows:\n\npaths:\n  /upload:\n    post:\n      summary: Upload files\n      description: Endpoint to upload images and a CSV file\n      requestBody:\n        required: true\n        content:\n          multipart/form-data:\n            schema:\n              type: object\n              properties:\n                image1:\n                  type: string\n                  format: binary\n                  contentEncoding: base64\n                image2:\n                  type: string\n                  format: binary\n                  contentEncoding: base64\n                csv:\n                  type: string\n                  contentMediaType: text/csv\n\n\nIn this updated example, the schema includes three properties: image1, image2, and csv. Each property has the type set to string, the format set to binary, and the contentEncoding set to base64. This allows you to upload images and a CSV file encoded in base64.\n\nRemember to adjust the endpoint path and other details according to your specific API requirements."
        },
        {
          "id": "openapi-v3.1-advanced-multiple-content-types",
          "title": "Multiple Content Types with OpenAPI",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/advanced/multiple-content-types/",
          "content": "Why Support Multiple Content Types?\n  Setting Up OpenAPI for Multiple Content Types    \n      Operation Definition\n      Schemas &amp; Examples\n    \n  \n\n\nLet’s dive into the nitty-gritty of describing multiple content types in OpenAPI. If you’ve ever needed to handle JSON, XML, CSV, and maybe even images in your API, you’re in the right place. This tutorial will guide you through setting up your OpenAPI document to gracefully handle multiple different formats.\n\nWhy Support Multiple Content Types?\n\nSupporting multiple content types in your API enhances client flexibility, interoperability, and user convenience. Different clients may prefer different data formats: JSON is great for web apps, XML might be preferred by legacy systems, and CSV is handy for data import/export tasks. By offering multiple formats, your API can interact with a broader range of systems and tools, making it more user-friendly and accessible.\n\nSetting Up OpenAPI for Multiple Content Types\n\nWe’ll start with a simple OpenAPI document for an endpoint that supports JSON, XML, and CSV.\n\nOperation Definition\n\nIn Understanding Structure &gt; Paths &amp; Operations we learned how to create a simple API with both application/json and text/csv, so lets take that example and expand it a little.\n\n    get:\n      summary: Get a list of bookings\n      responses:\n        '200':\n          description: A list of bookings\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Bookings'\n            application/xml:\n              schema:\n                $ref: '#/components/schemas/Bookings'\n            text/csv:\n              schema:\n                type: string\n              examples:\n                bookingsCsv:\n                  $ref: '#/components/examples/BookingsCSV'\n\n\nFor JSON and XML we can $ref to a schema, which could look a bit like this:\n\ncomponents:\n  schemas:\n    Bookings:\n      type: array\n      items:\n        type: object\n        properties:\n          id:\n            type: integer\n            format: int64\n          name:\n            type: string\n          price:\n            type: number\n            format: float\n\n\nThis is a generic enough data object that it would be fine to use for JSON and XML without any modifications. Here’s the output:\n\n[\n  {\n    \"id\": 0,\n    \"name\": \"string\",\n    \"price\": 0\n  }\n]\n\n\nThe XML it would look like this:\n\n&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;Bookings&gt;\n\t&lt;id&gt;0&lt;/id&gt;\n\t&lt;name&gt;string&lt;/name&gt;\n\t&lt;price&gt;0&lt;/price&gt;\n&lt;/Bookings&gt;\n\n\nThe API documentation tool has named the XML tag &lt;Bookings&gt; from the component name, but you can change that if learn about the xml keyword, which can also handle more advanced XML syntax.\n\nSchemas &amp; Examples\n\nOpenAPI doesn’t have native support for CSV schemas like it does for JSON and XML, but you can provide examples to illustrate the format.\n\ncomponents:\n  examples:\n    BookingsCSV:\n      summary: Example CSV output\n      value: |\n        id,name,price\n        1,Item 1,10.00\n        2,Item 2,15.00\n        3,Item 3,20.00\n\n\nHere’s the complete OpenAPI document:\n\nopenapi: 3.1.0\ninfo:\n  title: Multi-Content API\n  description: An API that supports JSON, XML, and CSV\n  version: 1.0.0\npaths:\n  /bookings:\n    get:\n      summary: Get a list of bookings\n      responses:\n        '200':\n          description: A list of bookings\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Bookings'\n            application/xml:\n              schema:\n                $ref: '#/components/schemas/Bookings'\n            text/csv:\n              schema:\n                type: string\n              examples:\n                bookingsCsv:\n                  $ref: '#/components/examples/BookingsCSV'\n\ncomponents:\n  schemas:\n    Bookings:\n      type: array\n      items:\n        type: object\n        properties:\n          id:\n            type: integer\n            format: int64\n          name:\n            type: string\n          price:\n            type: number\n            format: float\n  examples:\n    BookingsCSV:\n      summary: Example CSV output\n      value: |\n        id,name,price\n        1,Item 1,10.00\n        2,Item 2,15.00\n        3,Item 3,20.00\n\n\nYou now have an OpenAPI document that supports multiple content types, including JSON, XML, and CSV. By setting up your API this way, you can cater to a wider range of API clients and use cases, making your API more useful and user-friendly."
        },
        {
          "id": "openapi-v3.1-advanced-security",
          "title": "Security",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/advanced/security/",
          "content": "API Keys\n  HTTP Authentication    \n      Basic Authentication\n      Bearer Token Authentication\n      JWT Authentication\n    \n  \n  OAuth 2\n  OpenID Connect\n  Mutual TLS\n  Applying Security to Paths\n  Applying Security Globally\n  Overriding Path Security\n  Scopes\n\n\nOpenAPI uses the term “security scheme” to cover both authentication and authorization schemes.\n\n\n  Authentication: Who is this user, are they who they say they are.\n  Authorization: What data can this user see, what actions can they take.\n\n\nOpenAPI v3.1 lets you describe APIs protected using the following security schemes:\n\n\n  HTTP authentication schemes (anything using the Authorization header)\n    \n      Basic\n      Bearer\n      Digest\n      OAuth (1.0)\n      others defined in RFC 7235 and HTTP Authentication Scheme Registry\n    \n  \n  API keys in headers, query string or cookies\n  OAuth 2.x\n  OpenID Connect Discovery\n  Mutual TLS\n\n\nSome of these are very niche and specific forms of security scheme, so we won’t get stuck into all of them. Let’s look at the most common ones for now.\n\ncomponents:\n  securitySchemes:\n    # API Key Authentication\n    ApiKeyHeader:\n      type: apiKey\n      in: header\n      name: X-API-Key\n    ApiKeyQuery:\n      type: apiKey\n      in: query\n      name: key\n    \n    # HTTP Authentication\n    HttpBasicAuth:\n      type: http\n      scheme: basic\n    HttpBearerToken:\n      type: http\n      scheme: bearer\n    JWT:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\n   \n    # OAuth 2.0 Authentication\n    OAuth2ReadWrite:\n      type: oauth2\n      flows:\n        authorizationCode:\n          scopes:\n            read: Grants read access\n            write: Grants write access\n          authorizationUrl: https://example.com/oauth/authorize\n          tokenUrl: https://example.com/oauth/token\n          refreshUrl: https://example.com/oauth/refresh\n\n\nThis is 90% of what you’ll bump into in the wild when it comes to security schemes.\n\nAPI Keys\n\nin: header\n\nAPI Keys in headers are often stored in some arbitrary header name like X-API-Key, which is frowned upon but is still commonly used.\n\n  ApiKeyHeader:\n    type: apiKey\n    in: header\n    name: X-API-Key\n\n\nThat would describe a request that looks like this:\n\nGET /bookings\nHost: api.example.com\nX-API-Key: &lt;your-api-key&gt;\n\n\n\n  Instead of specifically using X- headers, you can create custom headers with your company name at the start like Stipe-API-Key if something is specific to you. If it’s not then it’s better to use the standard HTTP headers so generic tooling knows how to work with them out of the box.\n\n\nin: query\n\nAPI Keys in query are also common, but best avoided for other reasons: it’s incredibly insecure.\n\nGET /bookings?key=&lt;your-api-key&gt;\nHost: api.example.com\n\n\nThe OWASP API Security Top 10 lists Broken Authentication as one of the top security issues for APIs, citing specifically “Sends sensitive authentication details, such as auth tokens and passwords in the URL.”\n\nGenerally the “API Key” scheme is best used for legacy API security schemes, which are being deprecated and replaced with better security schemes.\n\nHTTP Authentication\n\nHTTP Authentication is a widely used security scheme in OpenAPI. It allows you to authenticate and authorize users based on the Authorization header in the HTTP request.\n\nBasic Authentication\n\nOne of the most common HTTP authentication schemes is Basic Authentication. It involves sending the username and password in the Authorization header, encoded in Base64. Here’s an example:\n\nHttpBasicAuth:\n  type: http\n  scheme: basic\n\n\nTo make a request using Basic Authentication, the Authorization header would look like this:\n\nGET /bookings\nHost: api.example.com\nAuthorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=\n\n\nThat dXNlcm5hbWU6cGFzc3dvcmQ= is base64 encoded username:password, which is exactly why this security scheme is considered unwise for sending long-lived credentials. Short-lived frequently changing tokens might be less problematic especially if being sent over TLS.\n\nBearer Token Authentication\n\nBearer Token Authentication is another popular HTTP authentication scheme. It involves sending a token in the Authorization header, prefixed with the word Bearer. Here’s an example:\n\nHttpBearerToken:\n  type: http\n  scheme: bearer\n\n\nTo make a request using Bearer Token Authentication, the Authorization header would look like this:\n\nGET /bookings\nHost: api.example.com\nAuthorization: Bearer &lt;your-token-here&gt;\n\n\nThis token is in plain text, but it’s usually randomly generated and short lives so it’s not so concerning if somebody nabs it, especially if you’re using JWT or OAuth 2 which operates over bearer.\n\nJWT Authentication\n\nUsing a JWT (JSON Web Token) allows for more advanced authentication scenarios and includes additional information in the token payload. Here’s an example:\n\nJWT:\n  type: http\n  scheme: bearer\n  bearerFormat: JWT\n\n\nIn OpenAPI a JWT security scheme is actually just Bearer Token Authentication again, with bearerFormat: JWT letting documentation tools know to mention that the token is a JWT. It doesn’t make much difference, its just how the token was generated before it was passed to the API.\n\nTo make a request using JWT Authentication, the Authorization header would look similar to Bearer Token Authentication:\n\nGET /bookings\nHost: api.example.com\nAuthorization: Bearer &lt;your-jwt-here&gt;\n\n\nOAuth 2\n\nOAuth 2 is a widely used authorization framework that allows users to grant third-party applications access to their resources without sharing their credentials. In OpenAPI, you can describe OAuth 2 authentication using the following example:\n\nOAuth2ReadWrite:\n  type: oauth2\n  flows:\n    authorizationCode:\n      scopes:\n        read: Grants read access\n        write: Grants write access\n      authorizationUrl: https://example.com/oauth/authorize\n      tokenUrl: https://example.com/oauth/token\n      refreshUrl: https://example.com/oauth/refresh\n\n\nIn this example, the OAuth2ReadWrite security scheme represents OAuth 2 authentication. It uses the authorizationCode flow, which is one of the most common flows in OAuth 2. The scopes section defines the available scopes for this security scheme, such as read and write. The authorizationUrl specifies the URL where users can authorize the application, the tokenUrl is used to obtain access tokens, and the refreshUrl is used to refresh expired tokens.\n\nTo apply OAuth 2 authentication to a specific operation, you can use the security keyword and specify the security scheme and scopes:\n\npaths:\n  /bookings:\n    get:\n      security:\n        - OAuth2ReadWrite: [read]\n\n\nIn this example, the /bookings path requires the OAuth2ReadWrite security scheme with the read scope for the GET operation.\n\nOpenID Connect\n\nOpenID Connect is an identity layer built on top of OAuth 2.0 that allows clients to verify the identity of end-users based on the authentication performed by an authorization server. It provides a standardized way to authenticate users and obtain their identity information, such as name, email, and profile picture.\n\nIn OpenAPI v3.1, you can describe OpenID Connect authentication using the following example:\n\nOpenIDConnect:\n  type: openIdConnect\n  openIdConnectUrl: https://example.com/.well-known/openid-configuration\n\n\nIn this example, the OpenIDConnect security scheme represents OpenID Connect authentication. The openIdConnectUrl specifies the URL where the OpenID Connect provider’s configuration can be found. This configuration includes important information such as the authorization endpoint, token endpoint, and public keys used for verifying ID tokens.\n\nTo apply OpenID Connect authentication to a specific operation, you can use the security keyword and specify the security scheme:\n\npaths:\n  /bookings:\n    get:\n      security:\n        - OpenIDConnect: []\n\n\nIn this example, the /bookings path requires the OpenIDConnect security scheme for the GET operation.\n\nOpenID Connect is a powerful authentication mechanism that allows you to leverage existing identity providers and provide a seamless login experience for your users. It is widely adopted and supported by major identity providers such as Google, Microsoft, and Okta.\n\nMutual TLS\n\nMutual TLS (Transport Layer Security) is an authentication type in OpenAPI that provides a secure way to authenticate both the client and the server. It involves the exchange of digital certificates between the client and the server to establish a trusted connection, meaning it’s commonly used in scenarios where strong authentication and secure communication are required: banking, healthcare, and other sensitive industries.\n\nWith mutual TLS authentication, the client presents its own certificate to the server during the TLS handshake process. The server then verifies the client’s certificate against its trusted certificate authority (CA) to ensure the client’s identity. Similarly, the server presents its own certificate to the client, and the client verifies the server’s certificate.\n\nThere’s not much to it in OpenAPI v3.1.\n\nMutualTLS:\n  type: mutualTLS\n\n\nThis is basically just a flag to let clients know they’ll need to set up Mutual TLS or no connections are going to work, but the specifics of that happen outside of OpenAPI. It takes a bit of work, but this authentication method adds an extra layer of security by ensuring that both the client and the server are authenticated before any data is exchanged. It helps prevent unauthorized access and protects against man-in-the-middle attacks.\n\nApplying Security to Paths\n\nThe security schemes are just setting there until they are applied to paths in your OpenAPI document. You can use the security keyword at the path level to do this:\n\nopenapi: 3.1.0\npaths:\n  /bookings:\n    get:\n      security:\n        - HttpBearerToken: []\n\n\nIn this example, the /bookings path requires the HttpBearerToken security scheme for all requests.\n\nopenapi: 3.1.0\npaths:\n  /bookings:\n    get:\n      security:\n        - ApiKeyQuery: []\n        - HttpBearerToken: []\n\n\nIn this example either the ApiKeyHeader or HttpBearerToken security schemes, meaning a client could use either. This is great when one method is being deprecated.\n\nSecurity schemes can also be applied as an AND:\n\nopenapi: 3.1.0\npaths:\n  /bookings:\n    get:\n      security:\n        - ApiKeyHeader: []\n          HttpBearerToken: []\n\n\nThis example would only pass if both the ApiKeyHeader and HttpBearerToken are passed.\n\nApplying Security Globally\n\nTo avoid needing to set similar security keywords on every path (and possibly missing a few) you can apply security globally.\n\nopenapi: 3.1.0\nsecurity:\n  - HttpBearerToken: []\npaths:\n  /bookings:\n    get:\n      # ...\n\n\nThis will now be applied to all paths unless turned off.\n\nOverriding Path Security\n\nGlobally applied rules can be overridden with a path-level security keyword.\n\nopenapi: 3.1.0\nsecurity:\n  - HttpBearerToken: []\npaths:\n  /bookings:\n    get:\n      security: [] # no security\n\n\nAll other paths will continue to be secured regardless of what order they’re in.\n\nScopes\n\nYou might have noticed the empty array showing up: HttpBearerToken: []. This empty array is where “scopes” go.\n\nScopes allow you to define fine-grained permissions within some types of security schema that support them, and in OpenAPI v3.1 that means OAuth 2 and OpenID Connect.\n\nEach security scheme can have its own set of scopes, which can be used to control access to specific resources or actions.\n\nOAuth2ReadWrite:\n  type: oauth2\n  flows:\n    authorizationCode:\n      scopes:\n        read: Grants read access\n        write: Grants write access\n\n\nIn this example, the OAuth2ReadWrite security scheme has two scopes: read and write, which can be used to check certain actions, maybe limiting GET to read and POST, PUT, PATCH, and DELETE to write.\n\nTo apply scopes to specific operations, you can use the security keyword and fill in that array at the end with the relevant scopes.\n\npaths:\n  /bookings:\n    get:\n      security:\n        - OAuth2ReadWrite: [read]\n    post:\n      security:\n        - OAuth2ReadWrite: [write]\n\n\nYou can get a lot more advanced with scopes than this, with some API developers breaking down the API into manageable chunks. Here are just a few from the Shopify API as an example:\n\nread_orders\nwrite_orders\n\nread_content\nwrite_content\n\nread_customers\nwrite_customers\n\nread_customer_payment_methods\nwrite_customer_payment_methods"
        },
        {
          "id": "openapi-v3.1-documentation-descriptions-and-summaries",
          "title": "Descriptions and Summaries",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/documentation/descriptions-and-summaries/",
          "content": "Example of Incomplete Documentation\n  Parameter Descriptions\n  Adding Descriptions to Tags\n  Operations\n  Responses\n\n\nOften, API documentation is left to the end of the schedule, which can lead to a rushed set of API reference documentation. This results in terse outlines of what endpoints and parameters exist, but leave out the specifics of your well-designed and implemented API operation. One of the major ways to improve the developer experience is to upgrade your OpenAPI from being a quick list of parameters into a store of import context and human knowledge with expanding descriptions and summaries.\n\nExample of Incomplete Documentation\n\nFor example, an operation might be described simply as GET /users with no further explanation, leaving users to guess what the endpoint does.\n\nExample Mistake:\n\npaths:\n  /bookings:\n    get:\n      summary: Get bookings\n      parameters:\n        - in: query\n          name: offset\n          schema:\n            type: integer\n            default: 10\n      responses:\n        200:\n          description: \"OK\"\n\n\nThis is barely useful. The summary is repeating “Get bookings” like thats not clear from the “get” and the “/bookings” which would be displayed in most documentation tools by default. The summary is a chance to add more human information, like an alt tag or a caption, for things that aren’t already there.\n\nDetailed and clear descriptions enhance understanding and usability. The improved version should thoroughly explain the operation, including its purpose, parameters, and response types.\n\nImproved Example:\n\npaths:\n  /bookings:\n    get:\n      summary: List bookings for user\n      description: Returns a paginated list of trip bookings by the authenticated user.\n      parameters:\n        - in: query\n          name: offset\n          schema:\n            type: integer\n            default: 10\n          description: &gt; \n            Used for pagination, the offset parameter allows you to skip \n            through the dataset to load the next set of records.\n      responses:\n        200:\n          description: \"A paginated list of bookings with detailed information.\"\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Bookings'\n\n\nLonger is not always better, and efforts to programmatically enforce the use of descriptions can lead to combative entries like “The User ID parameter contains an ID for a User” which no client wants to see. If you put some effort into building a culture around respecting the need for sharing context with your users, you can see quicker and more successful uptake of your API en masse. This has the handy benefit of reducing demands on the support teams as they have fewer confused users to help.\n\nThis example showed a few types of description, but you can put descriptions and summaries in quite a few places. Let’s take a look.\n\nParameter Descriptions\n\nParameters often seem really obvious to the author but not so much when somebody is quickly trying to integrate a single endpoint. For example, here origin and destination could be anything. Coordinates? City names? Station Codes? Station IDs?\n\n  /trips:\n    get:\n      parameters:\n        - name: origin\n          in: query\n          description: The ID of the origin station.\n          required: true\n          schema:\n            type: string\n            format: uuid\n        - name: destination\n          in: query\n          description: The ID of the destination station.\n          required: true\n          schema:\n            type: string\n            format: uuid\n\n\nHere the descriptions make that perfectly clear, regardless of which API documentation tool ends up being used to render this information.\n\nDescriptions are also a handy place to be more specific with criteria for the data that may have been vague otherwise.\n\n  /trips:\n    get:\n      parameters:\n      - name: date\n        in: query\n        description: The date and time of the trip in ISO 8601 format in origin station's timezone.\n        required: true\n        schema:\n          type: string\n          format: date-time\n          examples: ['2024-02-01T09:00:00Z']\n\n\nHere we have a format: date-time which should be displayed to users in API documentation and used in various other tools, and we have an example which shows what format it should look like, but if a user is then trying to configure their own code to use the right date format. If a client had to base it entirely off the example there is lots of room for error, but by clearly stating ISO 8601 in the description, a JavaScript user knows they could use dateObj.toISOString() instead of trying to make some other awkward format.\n\nAdding Descriptions to Tags\n\nTags are often underutilized, but they are a great way to group operations for more structured navigation in most API documentation tools. These tags are also a great place to write longer form context on what these tags actually mean in your domain specifically. For example, the word “Account” can mean 10 different things inside a large organization depending on the department and the function, so it’s good to be super clear about what they are.\n\ntags:\n  - name: Stations\n    description: | \n      Find and filter train stations across Europe, including their location\n      and local timezone.\n  - name: Trips\n    description: | \n      Timetables and routes for train trips between stations, including pricing\n      and availability.\n  - name: Bookings\n    description: | \n      Create and manage bookings for train trips, including passenger details\n      and optional extras.\n  - name: Payments\n    description: |\n      Pay for bookings using a card or bank account, and view payment\n      status and history.\n\n      &gt; warn\n      &gt; Bookings usually expire within 1 hour so you'll need to make your payment\n      &gt; before the expiry date \n\n\nHere not only are we explaining the words, but we are helping people find where particular information lives, and providing important context, like that Bookings usually expire within 1 hour which would not have been known to the client otherwise.\n\nOperations\n\nAPIs are rarely as simple as the CRUD (“Create, Read, Update, Delete”) lense many API developers naturally try and view them through. They can sometimes start out that way, but keep these things in mind:\n\n\n  Is the operation returning all records, or restricting data based on the authenticated user.\n  Is the operation using pagination or not.\n  Is there a default status being applied like status=active, and you need provide some other flag to change or remove the default.\n\n\nOnce you get into it, there’s usually quite a lot to say about an operation beyond “Get the Thing”.\n\n  /trips:\n    get:\n      summary: Get available train trips\n      description: Returns a list of available train trips between the specified origin and destination stations on the given date, and allows for filtering by bicycle and dog allowances.\n      parameters:\n        - name: origin\n          in: query\n          description: The ID of the origin station\n          required: true\n          schema:\n            type: string\n            format: uuid\n            examples: [efdbb9d1-02c2-4bc3-afb7-6788d8782b1e]\n        - name: destination\n          in: query\n          description: The ID of the destination station\n          required: true\n          schema:\n            type: string\n            format: uuid\n            examples: [b2e783e1-c824-4d63-b37a-d8d698862f1d]\n\n\nHere the summary is fairly light on details, because that’s more of a title for the operation, but you can often elude to a fair bit within a few words, then expand on all that in the description.\n\n  /bookings:\n    get:\n      summary: List bookings for user\n      description: Returns a paginated list of trip bookings by the authenticated user.\n\n\nResponses\n\nWhether to go large in the response or not is not so clear cut. If you were explaining what was going to happen in the operation description, then the response is… that, which has already been explained. Duplicating that long form description in the response seems redundant.\n\nGenerally, a common practice is to keep descriptions short, and you can go two ways with this:\n\n  /bookings:\n    get:\n      summary: List existing bookings\n      description: Returns a list of all trip bookings by the authenticated user.\n      responses:\n        '200':\n          description: A list of bookings\n        '401':\n          description: Unknown user error\n        '403':\n          description: Forbidden from seeing list of bookings\n\n\nThis all seems a bit redundant, because the operation happy path is described at the top, and it’s really the errors that need more explaining. Depending on how you have errors set up, they should be explaining themselves, so this is really either the place for context which could not exist anywhere else, or a chance to just keep it simple.\n\n  /bookings:\n    get:\n      summary: List existing bookings\n      description: Returns a list of all trip bookings by the authenticated user.\n      responses:\n        '200':\n          description: OK\n        '401':\n          description: Unauthorized\n        '403':\n          description: Forbidden\n\n\nThis is essentially just repeating the HTTP status code because you need to write something, but you can do whatever you like. Essentially the advice would be: keep response descriptions short, unless there’s some really important context which has to be given to that specific endpoint, and then do whatever your API documentation tool is happy with. If it prefers shorter descriptions maybe put that context in the operation description."
        },
        {
          "id": "openapi-v3.2-advanced-callbacks-webhooks",
          "title": "Callbacks and Webhooks",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/advanced/callbacks-webhooks/",
          "content": "Backstory\n  Callbacks\n  Webhooks\n  Consider AsyncAPI\n  Examples\n\n\nREST/HTTP APIs are often considered to be a simple request and response model, but they have always been a lot more asynchronous than that, and OpenAPI can capture two types of asynchronous HTTP event using callbacks and webhooks.\n\nBoth of these types of events are basically a HTTP request being made by your API, which is the opposite of all the other requests and responses being defined, but it works in a fairly familiar way.\n\nBackstory\n\nIn traditional request/response API designs, the client makes a request and the server responds. This works well for many use cases, but sometimes you need the server to notify the client about certain events asynchronously. This is where callbacks and webhooks come into play:\n\n\n  callbacks Callbacks allow an API to send real-time data back to the client as soon as the event has completed, and without the client having to check for progress. This is often used in tandem with 202 Accepted responses which defer the actually processing to a queue, promising to let you know when the work is complete.\n  webhooks Webhooks are similar to callbacks, but might not be happening in response to any one web request being made. It could be a generic “updates” integration point between multiple web services, with all sorts of events being transmitted as different happen throughout the system.\n\n\nIn OpenAPI v3.0 you only had callbacks, but OpenAPI v3.1 added support for webhooks too.\n\nCallbacks\n\nLet’s start by setting up a callback in OpenAPI. Suppose we have an API for creating orders, and we want to notify the client when the order status changes.\n\npaths:\n  /orders:\n    post:\n      summary: Create a new order\n      operationId: createOrder\n      requestBody:\n        description: Order object that needs to be added\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              properties:\n                id:\n                  type: string\n                item:\n                  type: string\n                quantity:\n                  type: integer\n                callbackUrl:\n                  type: string\n                  format: uri\n      responses:\n        '201':\n          description: Order created\n          content:\n            application/json:\n              schema:\n                type: object\n                properties:\n                  id:\n                    type: string\n                  status:\n                    type: string\n      callbacks:\n        onOrderStatusChange:\n          '{$request.body#/callbackUrl}':\n            post:\n              summary: Callback for order status change\n              requestBody:\n                description: Status update payload\n                required: true\n                content:\n                  application/json:\n                    schema:\n                      type: object\n                      properties:\n                        orderId:\n                          type: string\n                        status:\n                          type: string\n              responses:\n                '200':\n                  description: Callback processed successfully\n\n\nThis could be broken into two parts: creating a path for registering for the callbacks, in this case creating an order, then the definition of that callbacks themselves.\n\nCallbacks need a name, which is mainly used within OpenAPI and any tools which choose to find a use for it. This one is called onOrderStatusChange but you can use any naming convention you like.\n\nThe next bit is '{$request.body#/callbackUrl}' which is a runtime expression, that looks for a callbackUrl property in the HTTP request body.\n\nWhen the client sends a request to the API they need to let the API know the URL to send the callback to.\n\nPOST /orders HTTP/2\nHost: api.example.com\nContent-Type: application/json\n\n{\n  \"id\": \"12345\",\n  \"item\": \"Widget\",\n  \"quantity\": 1,\n  \"callbackUrl\": \"https://example.com/callbacks/order-status-updates\"\n}\n\n\nThe callbackURL could be in headers or could be composed of various other bits of data in the request, take a look at OpenAPI v3.1’s runtime expressions syntax to get more advanced.\n\nOnce you’ve got a callback named and figured out where the URL is going, the rest of the OpenAPI is going to feel very familiar, only it’s backwards.\n\nYou describe the request that your API is going to send, which is letting the client know what the HTTP request you’re sending will look like.\n\nThen you describe a response that your API is hoping to see their API return. You can get really specific with this or just specify that a 200 or 2XX is needed to let your API know the callback request is received properly. Most callback implementers will choose to retry these callbacks at a later date if the wrong status code appears, or if other success criteria fail errors can be sent to the authenticated users contact details.\n\nWebhooks\n\nTechnically callbacks are actually HTTP Webhooks in implementation, but in OpenAPI a “webhook” is specifically something named under the webhooks root of your OpenAPI document.\n\nThey are defined in a similar way to paths, but with a name instead of a URL.\n\nopenapi: 3.2.0\ninfo:\n  title: Train Travel API\n  version: 1.0.0\nwebhooks:\n  newBooking:\n    post:\n      summary: New Booking\n      description: Subscribe to new bookings being created, to update integrations for your users.\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/Booking'\n      responses:\n        '200':\n          description: Return a 200 status to indicate that the data was received successfully.\n\n\n\n  This example has trimmed the components section out for brevity, but you can imagine any schema you like going in there.\n\n\nBoth webhooks and paths are lists of Path Item Objects, similarly to callbacks, they are all just HTTP requests, some coming in (paths to be called), some going out (webhooks to be sent).\n\nThe key bits to notice here are:\n\n\n  newBooking is a unique nickname\n  Summary is a short title\n  Description is longer form documentation with Markdown covering context like “why” and edge cases to think of not covered elsewhere.\n  The requestBody is the request you’re going to send.\n  The responses property is a map of responses you want the client to return.\n\n\nThese then don’t need to be tied to paths. In fact, you can have an OpenAPI document which has no paths. This could be because you’re splitting your documents up in interesting reusable ways with these webhooks being shared across multiple APIs, or because your API is entirely asynchronous.\n\nConsider AsyncAPI\n\nIf you are using a few webhooks in an API which is largely otherwise driven by request/response, OpenAPI will be just fine for you with callbacks and webhooks helping as needed.\n\nHowever if you are building an API that is entirely asynchronous, you are using technologies like WebSockets, or are working with any other event-driven API protocols, you should consider using AsyncAPI for describing those parts of the architecture.\n\nExamples\n\nThere’s a full example in either Train Travel API, or Bump.sh API: every structural change on API documentation generates a new request to this webhook.\n\nYou can also find more information about documenting webhooks in this blog post."
        },
        {
          "id": "openapi-v3.2-advanced-error-formats",
          "title": "Handling Error Formats",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/advanced/error-formats/",
          "content": "API Errors with JSON Responses\n  Other Error Formats    \n      RFC 9457: Problem Details\n      JSON:API Errors\n    \n  \n\n\nWhen your API is happy you return the data or whatever response the action would like to provide, but what happens when you stray off that happy path and into the world of errors?\n\nIn HTTP an error is any response returning a 400-599 status code, but that alone is generally not enough to tell an API client what to do, why it happened, what can be done to avoid it, when to try again, or anything else.\n\nHTTP API errors usually involve some sort of JSON payload which explains a few key things:\n\n\n  \n    A human readable short summary: “Cannot checkout with an empty shopping cart”\n  \n  \n    A human readable message: “It looks like you have tried to check out but there is nothing in your…”\n  \n  \n    An application-specific error code relating to the problem: ERRCARTEMPTY\n  \n  \n    Links to a documentation page or knowledge base where a client or user of the client can figure out what to do next.\n  \n\n\nAPI Errors with JSON Responses\n\nThe most basic API error might look a bit like this, with something for the humans to read, a specific error code for a machine to read that should describe a more specific situation to them (perhaps its documented somewhere), and/or a unique link to a specific error which can help a machine recognize the exact application-level problem from a predefined list.\n\nHTTP/2 400 Bad Request\nContent-Type: application/json\n\n{\n  \"error\": {\n    \"message\": \"Message describing the error\",\n    \"code\": \"ERR-01234\",\n    \"href\": \"http://example.org/docs/errors/#ERR-01234\"\n  }\n}\n\n\nThis JSON structure is not perfect but it’s realistic, so lets show how to describe it.\n\npaths:\n  /bookings:\n    get:\n      responses:\n        '200':\n          description: OK\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Booking'\n        '400':\n          $ref: '#/components/responses/BadRequest'\n        '401':\n          $ref: '#/components/responses/Unauthorized'\n        '404':\n          $ref: '#/components/responses/NotFound'\n        '409':\n          $ref: '#/components/responses/Conflict'\n        '429':\n          $ref: '#/components/responses/TooManyRequests'\n        '500':\n          $ref: '#/components/responses/InternalServerError'\n\n\nThis operation shows a success status of 201, and a whole variety of interesting potential errors that can be returned. Any HTTP interaction could have a 500 so maybe you don’t need to mention that, and there could be infinite other errors happening between your server and the client that would be impossible to guess, but if you aim to define as many errors for any given operation as seem relevant, you aren’t going to be far off.\n\nTo avoid repetition and ensure consistency, this example defines reusable HTTP error responses in the components section. These reusable components can then be referenced in multiple operations.\n\ncomponents:\n  schema:\n    Problem:\n      properties:\n        message:\n          type: string\n          description: An explanation of the problem.\n          example: Not enough credits in your account balance.\n        code:\n          type: string\n          description: An error code relating to an application-specific error scenario\n          example: ERR-01234\n        href:\n          type: string\n          format: url\n          description: A URL to find some more documentation on this problem if needed.\n          example: http://example.org/docs/errors/#ERR-01234\n\n  responses:\n    BadRequest:\n      description: Bad Request\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n          example:\n            message: The request is invalid or missing required parameters.\n            code: ERR-NAUGHTY001\n            href: https://example.com/docs/errors/ERR-NAUGHTY001\n\n    Conflict:\n      description: Conflict\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n          example:\n            message: There is a conflict with an existing resource.\n            code: ERR-EYYWOAH001\n            href: https://example.com/docs/errors/ERR-EYYWOAH001\n\n    Forbidden:\n      description: Forbidden\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n          example:\n            message: Access is forbidden with the provided credentials.\n            code: ERR-NAH001\n            href: https://example.com/docs/errors/ERR-NAH001\n\n    InternalServerError:\n      description: Internal Server Error\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n          example:\n            message: An unexpected error occurred.\n            code: ERR-OOF001\n            href: https://example.com/docs/errors/ERR-OOF001\n\n\nThis approach is one of many you could take, but essentially what we’re doing here is using one generic schema, then providing a tailored example for each type of error, with the types of error corresponding so far to the HTTP status codes likely to be returned.\n\nYou could get more specific and get into application-level errors here, but they might be better off left as examples and the specific errors all defined elsewhere, the same place that is being linked to with these href values.\n\nOther Error Formats\n\nAPI errors can be designed in a bunch of ways, and described in a bunch of ways, but as with most things in APIs: there’s a standard for that! Actually… there’s a few.\n\nRFC 9457: Problem Details\n\nRFC 9457: Problem Details for HTTP APIs (replacing very similar RFC 7807) defines loads of useful ideas and structure for API error messages. If we want to describe this, we can expand the main problem object from the last example.\n\ncomponents:\n  schema:\n    Problem:\n      properties:\n        type:\n          type: string\n          description: A URI reference that identifies the problem type\n          example: https://example.com/probs/out-of-credit\n        title:\n          type: string\n          description: A short, human-readable summary of the problem type\n          example: You do not have enough credit.\n        detail:\n          type: string\n          description: A human-readable explanation specific to this occurrence of the problem\n          example: Your current balance is 30, but that costs 50.\n        instance:\n          type: string\n          description: A URI reference that identifies the specific occurrence of the problem\n          example: /account/12345/msgs/abc\n        status:\n          type: integer\n          description: The HTTP status code\n          example: 400\n\n\nThen as before you can expand on that schema with better examples.\n\n  responses:\n    BadRequest:\n      description: Bad Request\n      content:\n        application/problem+json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n          example:\n            type: https://example.com/errors/bad-request\n            title: Bad Request\n            status: 400\n            detail: The request is invalid or missing required parameters.\n        \n    Conflict:\n      description: Conflict\n      content:\n        application/problem+json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n          example:\n            type: https://example.com/errors/conflict\n            title: Conflict\n            status: 409\n            detail: There is a conflict with an existing resource.\n     \n    Forbidden:\n      description: Forbidden\n      content:\n        application/problem+json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n          example:\n            type: https://example.com/errors/forbidden\n            title: Forbidden\n            status: 403\n            detail: Access is forbidden with the provided credentials.\n     \n    InternalServerError:\n      description: Internal Server Error\n      content:\n        application/problem+json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n          example:\n            type: https://example.com/errors/internal-server-error\n            title: Internal Server Error\n            status: 500\n            detail: An unexpected error occurred.\n\n\nHere the application/problem+json content type lets everyone know this is specifically using that RFC, the URIs replace both the status code and the link to documentation, and a title + detail allow room for a generic error message and instance specific details about the exact problem happening right now.\n\nJSON:API Errors\n\nAnother popular format for API errors is the JSON:API errors format. This format provides a standardized structure for representing errors in JSON responses.\n\nThe JSON:API errors format includes the following key components:\n\n\n  An array of error objects, each representing a specific error.\n  Each error object contains the following properties:\n    \n      id: A unique identifier for the error.\n      status: The HTTP status code associated with the error.\n      code: An application-specific error code.\n      title: A short, human-readable summary of the error.\n      detail: A detailed description of the error.\n      source: Information about the source of the error, such as the field or parameter that caused the error.\n    \n  \n\n\nThis is a little different as its an array of errors, but here’s how we can handle that with OpenAPI:\n\ncomponents:\n  schema:\n    Problem:\n      type: object\n      properties:\n        errors:\n          type: array\n          items:\n            type: object\n            properties:\n              id:\n                type: string\n                format: uuid\n                description: A unique identifier for the error.\n                examples: [\"2fe04316-9775-46af-987b-a12d8620d42e\"]\n              status:\n                type: string\n                description: The HTTP status code associated with the error.\n                examples: [\"404\"]\n              code:\n                type: string\n                description: An application-specific error code.\n                examples: [ERR-NOT-FOUND]\n              title:\n                type: string\n                description: A short, human-readable summary of the error.\n                examples: [Resource Not Found]\n              detail:\n                type: string\n                description: A detailed description of the error.\n                examples: [The requested resource could not be found.]\n              source:\n                type: object\n                properties:\n                  pointer:\n                    type: string\n                    description: Information about the source of the error, such as the field or parameter that caused the error.\n                    examples: [\"/data/id\"]\n                  parameter:\n                    type: string\n                    description: Information about the source of the error, such as the field or parameter that caused the error.\n                    examples: [id]\n\n\nThe fact that the schema is an array does not change the previous approach for describing errors, we just move that into the example:\n\n  responses:\n    BadRequest:\n      description: Bad Request\n      content:\n        application/vnd.api+json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n          example:\n            errors:\n              - id: \"2fe04316-9775-46af-987b-a12d8620d42e\"\n                status: \"400\"\n                code: \"ERR-BAD-REQUEST\"\n                title: \"Bad Request\"\n                detail: \"The request contained an id with an integer but its meant to be a UUID\"\n                source:\n                  pointer: \"/data/id\"\n                  parameter: \"id\"\n            \n    Conflict:\n      description: Conflict\n      content:\n        application/vnd.api+json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n          example:\n            errors:\n              - id: \"2fe04316-9775-46af-987b-a12d8620d42e\"\n                status: \"409\"\n                code: \"ERR-CONFLICT\"\n                title: \"Bad Request\"\n                detail: &gt; \n                  There is a conflict with an existing resource, the approved status\n                  cannot be changed back to pending.\n                source:\n                  pointer: \"/data/status\"\n                  parameter: \"status\"\n     \n    Forbidden:\n      description: Forbidden\n      content:\n        application/vnd.api+json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n          example:\n            errors:\n              - id: \"2fe04316-9775-46af-987b-a12d8620d42e\"\n                status: \"403\"\n                code: \"ERR-FORBIDDEN\"\n                title: \"Access is forbidden with the provided credentials.\"\n                detail: &gt; \n                  This action cannot be undertaken for this particular reason.\n                source:\n                  pointer: \"/header/Authorization\"\n                  parameter: \"Authorization\"\n\n\nIf you need to handle errors responses in multiple content types, you can learn about Multiple Content Types and it’s all the same."
        },
        {
          "id": "openapi-v3.2-advanced-file-uploads",
          "title": "Uploading a File",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/advanced/file-uploads/",
          "content": "APIs can handle file uploads in a variety of ways, and OpenAPI can help you describe any of them. The two most common methods are directly accepting the file based on the content-type of the request, e.g.: a HTTP request with image/png, image/jpeg, text/csv, etc., or a HTTP request with a multipart/form-data content type which allows you to combine text and file data in a single request.\n\nHere’s an example of accepting a CSV file being directly passed in the request body, showing how the HTTP request would look, then how that would be described in OpenAPI v3.1.\n\nGET /upload HTTP/2\nHost: api.example.org\nContent-Type: text/csv\n\nPlanted On,Longitude,Latitude,Unit Type,Species\n4/1/23,-3.88628,56.17454,tree,Aspen\n4/1/23,-3.8863,56.17455,tree,Silver Birch\n\n\npaths:\n  /upload:\n    post:\n      summary: Upload a CSV file\n      requestBody:\n        required: true\n        content:\n          text/csv: {}\n\n\nThe text/csv does not need to declare a schema if its transferred in a binary (octet-stream), as that is the default. The same goes for images.\n\ncontent:\n    image/png:\n        schema:\n            type: string\n            contentMediaType: image/png\n            contentEncoding: base64\n\n\npaths:\n  /upload:\n    post:\n      summary: Upload a file\n      description: Endpoint to upload a file\n      requestBody:\n        required: true\n        content:\n          multipart/form-data:\n            schema:\n              type: object\n              properties:\n                file:\n                  type: string\n                  contentMediaType: text/csv\n\n\nIn this example, the requestBody object specifies that the request body is required and should be in the multipart/form-data format. The schema defines an object with a single property file, which represents the uploaded file. The type is set to string and the format is set to binary to indicate that it is a binary file.\n\nTo upload images and a CSV file using the contentEncoding keyword, you can modify the schema as follows:\n\npaths:\n  /upload:\n    post:\n      summary: Upload files\n      description: Endpoint to upload images and a CSV file\n      requestBody:\n        required: true\n        content:\n          multipart/form-data:\n            schema:\n              type: object\n              properties:\n                image1:\n                  type: string\n                  format: binary\n                  contentEncoding: base64\n                image2:\n                  type: string\n                  format: binary\n                  contentEncoding: base64\n                csv:\n                  type: string\n                  contentMediaType: text/csv\n\n\nIn this updated example, the schema includes three properties: image1, image2, and csv. Each property has the type set to string, the format set to binary, and the contentEncoding set to base64. This allows you to upload images and a CSV file encoded in base64.\n\nRemember to adjust the endpoint path and other details according to your specific API requirements."
        },
        {
          "id": "openapi-v3.2-advanced-multiple-content-types",
          "title": "Multiple Content Types with OpenAPI",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/advanced/multiple-content-types/",
          "content": "Why Support Multiple Content Types?\n  Setting Up OpenAPI for Multiple Content Types    \n      Operation Definition\n      Schemas &amp; Examples\n    \n  \n\n\nLet’s dive into the nitty-gritty of describing multiple content types in OpenAPI. If you’ve ever needed to handle JSON, XML, CSV, and maybe even images in your API, you’re in the right place. This tutorial will guide you through setting up your OpenAPI document to gracefully handle multiple different formats.\n\nWhy Support Multiple Content Types?\n\nSupporting multiple content types in your API enhances client flexibility, interoperability, and user convenience. Different clients may prefer different data formats: JSON is great for web apps, XML might be preferred by legacy systems, and CSV is handy for data import/export tasks. By offering multiple formats, your API can interact with a broader range of systems and tools, making it more user-friendly and accessible.\n\nSetting Up OpenAPI for Multiple Content Types\n\nWe’ll start with a simple OpenAPI document for an endpoint that supports JSON, XML, and CSV.\n\nOperation Definition\n\nIn Understanding Structure &gt; Paths &amp; Operations we learned how to create a simple API with both application/json and text/csv, so lets take that example and expand it a little.\n\n    get:\n      summary: Get a list of bookings\n      responses:\n        '200':\n          description: A list of bookings\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Bookings'\n            application/xml:\n              schema:\n                $ref: '#/components/schemas/Bookings'\n            text/csv:\n              schema:\n                type: string\n              examples:\n                bookingsCsv:\n                  $ref: '#/components/examples/BookingsCSV'\n\n\nFor JSON and XML we can $ref to a schema, which could look a bit like this:\n\ncomponents:\n  schemas:\n    Bookings:\n      type: array\n      items:\n        type: object\n        properties:\n          id:\n            type: integer\n            format: int64\n          name:\n            type: string\n          price:\n            type: number\n            format: float\n\n\nThis is a generic enough data object that it would be fine to use for JSON and XML without any modifications. Here’s the output:\n\n[\n  {\n    \"id\": 0,\n    \"name\": \"string\",\n    \"price\": 0\n  }\n]\n\n\nThe XML it would look like this:\n\n&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;Bookings&gt;\n\t&lt;id&gt;0&lt;/id&gt;\n\t&lt;name&gt;string&lt;/name&gt;\n\t&lt;price&gt;0&lt;/price&gt;\n&lt;/Bookings&gt;\n\n\nThe API documentation tool has named the XML tag &lt;Bookings&gt; from the component name, but you can change that if learn about the xml keyword, which can also handle more advanced XML syntax.\n\nSchemas &amp; Examples\n\nOpenAPI doesn’t have native support for CSV schemas like it does for JSON and XML, but you can provide examples to illustrate the format.\n\ncomponents:\n  examples:\n    BookingsCSV:\n      summary: Example CSV output\n      value: |\n        id,name,price\n        1,Item 1,10.00\n        2,Item 2,15.00\n        3,Item 3,20.00\n\n\nHere’s the complete OpenAPI document:\n\nopenapi: 3.2.0\ninfo:\n  title: Multi-Content API\n  description: An API that supports JSON, XML, and CSV\n  version: 1.0.0\npaths:\n  /bookings:\n    get:\n      summary: Get a list of bookings\n      responses:\n        '200':\n          description: A list of bookings\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Bookings'\n            application/xml:\n              schema:\n                $ref: '#/components/schemas/Bookings'\n            text/csv:\n              schema:\n                type: string\n              examples:\n                bookingsCsv:\n                  $ref: '#/components/examples/BookingsCSV'\n\ncomponents:\n  schemas:\n    Bookings:\n      type: array\n      items:\n        type: object\n        properties:\n          id:\n            type: integer\n            format: int64\n          name:\n            type: string\n          price:\n            type: number\n            format: float\n  examples:\n    BookingsCSV:\n      summary: Example CSV output\n      value: |\n        id,name,price\n        1,Item 1,10.00\n        2,Item 2,15.00\n        3,Item 3,20.00\n\n\nYou now have an OpenAPI document that supports multiple content types, including JSON, XML, and CSV. By setting up your API this way, you can cater to a wider range of API clients and use cases, making your API more useful and user-friendly."
        },
        {
          "id": "openapi-v3.1-documentation-external-documentation",
          "title": "External Documentation",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/documentation/external-documentation/",
          "content": "While the description property is excellent for giving a little more information about a specific tag, you might need to provide additional documentation if the business logic by a part of the API is complex, or there are lists of possible values defined outside of the API.\n\nIf anything requires further explanation, you can provide a link to an external web page where you offer a more detailed explanation using the externalDocs property.\n\nThe externalDocs property is two things, a URL using the url property, and a description explaining what this link is about.\n\ntags:\n  - name: Diffs\n    description: Diff summary of changes in the API\n    externalDocs:\n      description: More details about Diff\n      url: https://docs.bump.sh/help/api-change-management/\n\n\n\nThis is not limited to tags, externalDocs can be used on:\n\n\n  Root Object\n  Tag Object\n  Operation Object\n  Schema Object\n\n\nHere’s all of them being used all at once!\n\nopenapi: 3.1.0\ninfo:\n  title: External Docs Everywhere!\n  version: \"1.0.0\"\n\nexternalDocs:\n  description: Guides &amp; Tutorials\n  url: https://docs.bump.sh/guides/\n\npaths:\n  /diffs:\n    get:\n      externalDocs:\n        description: Learn more about Operations\n        url: https://docs.bump.sh/openapi/v3.1/understanding-structure/paths-operations/\n\ntags:\n  - name: Diffs\n    description: Diff summary of changes in the API\n    externalDocs:\n      description: More details about Diff\n      url: https://docs.bump.sh/help/api-change-management/\n\ncomponents:\n  schemas:\n    Diffs:\n      externalDocs:\n        url: https://docs.bump.sh/openapi/v3.1/data-models/schema-and-data-types/\n\n\n\nWhen you generate API documentation for the API description above, you’ll see the link rendered like this:"
        },
        {
          "id": "openapi-v3.1-documentation-grouping-operations-with-tags",
          "title": "Organize API Endpoints with OpenAPI Tags",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/documentation/grouping-operations-with-tags/",
          "content": "Referencing Tags in Endpoints\n  Benefits of OpenAPI Tags    \n      Tags Can Describe Endpoint Groups\n      Tags Can Link to Additional Documentation\n      Tags Can Order Endpoint Groups in Documentation\n    \n  \n  OpenAPI Tags Best Practices    \n      Tag Everything\n      Make Every Tag Unique\n      Define All Your OpenAPI Tags in the Root Tag Object\n    \n  \n  Conclusion\n\n\nTags are a great way to organize the API endpoints in your OpenAPI documents.\n\nTypically, OpenAPI tags are used to group related endpoints in a meaningful way, such as by business function or logical objects. When using tags, you define an array of tags at the root of your document, like this:\n\ntags:\n  - name: stations\n    summary: Stations\n    description: | \n      Find and filter train stations across Europe, including their location\n      and local timezone.\n    externalDocs:\n      description: Read more\n      url: http://docs.example.com/guides/stations\n  - name: trips\n    summary: Trips\n    description: |\n      Timetables and routes for train trips between stations, including pricing\n      and availability.\n  - name: bookings\n    summary: Bookings\n    description: | \n      Create and manage bookings for train trips, including passenger details\n      and optional extras.\n  - name: payments\n    summary: Payments\n    description: |\n      Pay for bookings using a card or bank account, and view payment\n      status and history.\n\n      &gt; warn\n      &gt; Bookings usually expire within 1 hour so you'll need to make your payment\n      &gt; before the expiry date \n\n\nThe name property is required and must be unique across all tags in the document. It is used to reference the tag in the tags property of an endpoint, and is generally considered as a variable name for the tag.\n\nOpenAPI v3.2 introduced the summary property, which is a human-readable short description of the tag. Often this is just a Case Title version of the name but it can be anything, just keep it short so it fits nicely into API reference documentation navigation menus as that’s generally what it’s used for.\n\nThe description property can be used to provide more detailed information about the tag. This can be really in depth and fully explain what the concept means, because e.g: Account or Unit could mean infinite different things in different domains.\n\nYou can also link to external documentation using the externalDocs property.\n\nReferencing Tags in Endpoints\n\nOnce you’ve created these tags, you can use them to group related endpoints in your API using the tags property on the endpoint as follows:\n\npaths:\n  /stations:\n    get:\n      summary: Get a list of train stations\n      tags: [ stations ]\n  /trips:\n    get:\n      summary: Get available train trips\n      tags: [ trips ]\n\n\nYou can also apply multiple tags to an operation:\n\npaths:\n  /bookings/{bookingId}/payment:\n    post:\n      summary: Pay for a Booking\n      tags: [ bookings, payments ]\n\n\nBenefits of OpenAPI Tags\n\nTags are a powerful tool for improving the usability of your OpenAPI document. Below are some of the ways using tags can help keep your OpenAPI document organized.\n\nTags Can Describe Endpoint Groups\n\nWhen specifying your tags in the root level of your API contract, you can give context to the tag using the description property.\n\nLet’s take Bump.sh API documentation. Here is how the Diffs tag is created and described in Bump.sh API Contract:\n\ntags:\n  - name: diff\n    summary: Diffs\n    description: Diff summary of changes in the API\n\n\nThe documentation will show the diff tag like this:\n\n\nSee it live\n\nNote that you can use markdown in the description field to better describe your tags.\n\nTags Can Link to Additional Documentation\n\nWhile the description property is excellent for giving a little more information about a specific tag, you might need to provide additional documentation if the business logic or object represented by the tag is complex and requires further explanation. Let’s take our Diffs example from above. You can provide a link to an external web page where you offer a more detailed explanation using the externalDocs property.\n\nIn the code snippet below, the externalDocs property provides a link to a URL using the url property. A description for the URL can also be specified using the description property.\n\ntags:\n  - name: diff\n    description: Diff summary of changes in the API\n    externalDocs:\n      description: More details about Diff\n      url: /help/api-change-management/\n\n\nWhen you generate API documentation for the API contract above, you’ll see the link rendered like this:\n\n\n\nTags Can Order Endpoint Groups in Documentation\n\nWhen specifying your OpenAPI or AsyncAPI tags in the root of your API contract, the order in which you list the tags will define the order in which they appear in the generated documentation. This ordering lets you sort the tags meaningfully.\n\ntags:\n  - name: diff\n    summary: Diffs\n    description: Diff summary of changes in the API\n  - name: ping\n    summary: Ping\n    description: Monitoring status endpoints\n  - name: preview\n    summary: Previews\n    description: Preview for documentation file\n  - name: version\n    summary: Versions\n    description: Deploy your API contracts\n  - name: validation\n    summary: Validations\n    description: Check &amp; validate your API contracts\n  - name: hub\n    summary: Hubs\n    description: Interact with your Hubs\n\n\nWhen you generate API documentation, you’ll notice the documentation orders the endpoint groups in the same way:\n\n\nSee it live\n\nNote that Bump.sh helps you order your endpoints and webhooks using a “Group by tag” operation. It is actually the default behaviour of Bump.sh when you have these tags defined and have not selected an other sorting option for your Bump.sh API documentation.\n\nNow that you understand what tags are and their benefits, you’ll see some best practices you should follow when using OpenAPI tags in API contracts.\n\nOpenAPI Tags Best Practices\n\nTag Everything\n\nWhen using tags, make sure you tag all your endpoints.\n\nNotice how all diff-related endpoints are tagged with the diffs tag in this snippet:\n\npaths:\n  /diffs:\n    post:\n      tags: [ diff ]\n      summary: Create a diff\n      [...]\n  /diffs/{id}:\n    get:\n      tags: [ diff ]\n      summary: Fetch detailed information from an existing diff\n      [...]\n\n\nYou can see live how they are all available under the section Diffs. By clicking the name of the section in the left menu, the tagged endpoints will show up.\n\n\n\nUntagged endpoints will not show up under any big section represented by a tag of your documentation generated by Bump.sh\n\nTo ensure your endpoints remain logically grouped and ordered, always tag every endpoint, even if it means creating a tag for a single endpoint.\n\nMake Every Tag Unique\n\nWhen defining the list of tags in the root of your API contract, make sure not to duplicate tag names. Since the tag’s name property links an endpoint to a tag, duplicate names are likely to confuse developers looking at the API contract.\n\nThe code snippet below contains the root Tag Object in an API contract. Notice how the validation tag has been duplicated, and the second definition contains a different description to the first:\n\ntags:\n  - name: diff\n    summary: Diffs\n    description: Diff summary of changes in the API.\n  - name: version\n    summary: Versions\n    description: Deploy your API contracts.\n  - name: validation\n    summary: Validations\n    description: Check &amp; validate your API contracts.\n  - name: documentation_change\n    summary: Documentation Change\n    description: Check &amp; validate your API contracts.\n  - name: validation\n    summary: Validations\n    description: Validate your API status.\n\n\nThese duplicate tags would confuse anyone trying to understand your API contract, as they wouldn’t know which of the two tag definitions an endpoint belongs to.\n\nInstead, make sure you define and describe every tag only once in the root Tag Object, like in the snippet below:\n\ntags:\n  - name: diff\n    summary: Diffs\n    description: Diff summary of changes in the API.\n  - name: version\n    summary: Versions\n    description: Deploy your API contracts.\n  - name: validation\n    summary: Validations\n    description: Check &amp; validate your API contracts.\n  - name: documentation_change\n    summary: Documentation Change\n    description: Check &amp; validate your API contracts.\n\n\nDefine All Your OpenAPI Tags in the Root Tag Object\n\nThe OpenAPI specification doesn’t require you to define all your tags in the root Tag Object of your API contract. This means you can add a tag to an endpoint without listing it in the root Tag Object, but this is a bad idea. You won’t be able to control what order the OpenAPI tags should appear in, and you won’t be able to add a description or provide a link to external documentation for that tag. It can also confuse developers browsing your API contract as they won’t see a list of all the tags used in the API contract.\n\nAs an example, consider the code snippet below where the Previews and the Ping tags has not been included in the root Tag Object:\n\ntags:\n  - name: Diffs\n    description: Diff summary of changes in the API\n  # Missing Previews tag\n  # Missing Ping tag\n  - name: Versions\n    description: Deploy your API contracts\n  - name: Validations\n    description: Check &amp; validate your API contracts\n  - name: Hubs\n    description: Interact with your Hubs\n\npaths:\n  /diffs:\n    post:\n      tags: [ Diffs ]\n  /diffs/{id}:\n    get:\n      tags: [ Diffs ]\n  /hubs/{hub_id_or_slug}:\n    get:\n      tags: [ Hubs ]\n  /versions:\n    post:\n      tags: [ Versions ]\n  /validations:\n    post:\n      tags: [ Validations ]\n  /previews:\n    post:\n      tags: [ Previews ]\n  /previews/{preview_id}:\n    put:\n      tags: [ Previews ]\n  /versions/{version_id}:\n    get:\n      tags: [ Versions ]\n  /ping:\n    get:\n      tags: [ Ping ]\n\n\nWhen you generate the documentation, notice how the Previews and Ping sections are at the bottom of the list.\n\n\n\nThis incorrect ordering and lack of description will make this section much harder to understand for a developer consuming your API.\n\nOn the other hand, notice how every endpoint in the API contract below has a tag also defined in the root Tag Object:\n\ntags:\n  - name: Diffs\n    description: Diff summary of changes in the API\n  - name: Ping\n    description: Check the API status\n  - name: Previews\n    description: Preview changes to an API Documentation\n  - name: Versions\n    description: Deploy your API contracts\n  - name: Validations\n    description: Check &amp; validate your API contracts\n  - name: Hubs\n    description: Interact with your Hubs\n\npaths:\n  /diffs:\n    post:\n      tags: [ Diffs ]\n  /diffs/{id}:\n    get:\n      tags: [ Diffs ]\n  /hubs/{hub_id_or_slug}:\n    get:\n      tags: [ Hubs ]\n  /versions:\n    post:\n      tags: [ Versions ]\n  /validations:\n    post:\n      tags: [ Validations ]\n  /previews:\n    post:\n      tags: [ Previews ]\n  /previews/{preview_id}:\n    put:\n      tags: [ Previews ]\n  /versions/{version_id}:\n    get:\n      tags: [ Versions ]\n  /ping:\n    get:\n      tags: [ Ping ]\n\n\nBy doing this, your documentation will display the endpoint groups in the correct order along with the tag’s description.\n\n\n\nConclusion\n\nIn this article, you learned more about OpenAPI tags and their value in an API contract. You also learned that you can add descriptions and external documentation links to the tag. This article has also shown you some best practices to follow when using tags that can improve the quality of your generated documentation."
        },
        {
          "id": "openapi-v3.1-cheatsheet",
          "title": "OpenAPI 3.1 - The Cheat Sheet",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/cheatsheet/",
          "content": "Now that you’ve roamed through the Complete Guide, let’s make long stories short. We have listed the key elements you should always keep in mind when writing a new OpenAPI contract, or maintaining existing ones.\n\n\nDownload the PDF version\n\nThe Cheat Sheet is presented here in an initial version.\n\nHere are the current sections:\n\n  Document Structure\n  General Information\n  API Structure\n  Data Types and Schemas\n  Security\n  Reuse Elements\n  Polymorphism\n  Grouping and sorting\n\n\nFor any feedback and suggestions, please open an issue on our GitHub Repository. We are currently working on building a web version of the Cheat Sheet, so that anyone can directly contribute to it with a Pull Request."
        },
        {
          "id": "openapi-v3.2-cheatsheet",
          "title": "OpenAPI 3.1 - The Cheat Sheet",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/cheatsheet/",
          "content": "Now that you’ve roamed through the Complete Guide, let’s make long stories short. We have listed the key elements you should always keep in mind when writing a new OpenAPI contract, or maintaining existing ones.\n\n\nDownload the PDF version\n\nThe Cheat Sheet is presented here in an initial version.\n\nHere are the current sections:\n\n  Document Structure\n  General Information\n  API Structure\n  Data Types and Schemas\n  Security\n  Reuse Elements\n  Polymorphism\n  Grouping and sorting\n\n\nFor any feedback and suggestions, please open an issue on our GitHub Repository. We are currently working on building a web version of the Cheat Sheet, so that anyone can directly contribute to it with a Pull Request."
        },
        {
          "id": "arazzo-v1.0",
          "title": "Arazzo Specification Complete Guide",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/arazzo/v1.0/",
          "content": "Arazzo is an open standard from the OpenAPI Initiative for describing API workflows. While OpenAPI documents what each endpoint does individually, Arazzo documents how endpoints work together to accomplish real tasks like booking a trip, processing a payment, or onboarding a user.\n\nA single Arazzo document can power documentation, integration tests, production monitoring, and automation. This guide covers every aspect of the Arazzo 1.0 specification.\n\nIntroduction\n\n\n  What is Arazzo?: what problems Arazzo solves, how it compares to previous approaches (written docs, sample code, proprietary tools), and what a workflow document looks like in practice.\n  History and evolution: from the first OpenAPI community discussions in 2021 to the official 1.0.0 release in 2024, and what is planned for version 1.1.\n  Benefits of using Arazzo: how Arazzo improves documentation, testing, governance, monitoring, and cross-API orchestration, with concrete examples for each use case.\n\n\nUnderstanding Arazzo structure\n\nEvery Arazzo document is built from five building blocks: a version declaration, metadata, source descriptions, workflows, and optional reusable components.\n\n\n  Basic structure: the top-level anatomy of an Arazzo document, with the arazzo, info, sourceDescriptions, workflows, and components sections.\n  Defining sources: how to reference OpenAPI documents and other Arazzo files, enable cross-API orchestration across multiple services, and compose workflows from separate files.\n  Workflows: designing workflow sequences with typed inputs (JSON Schema), computed outputs, global parameters, and dependency chains between workflows.\n  Steps, inputs, and outputs: how each step references an API operation, passes parameters and request bodies, extracts values from responses with JSON Pointer, and feeds data to the next step.\n  Success and failure: defining business-level success criteria beyond HTTP status codes, branching with goto, retrying with backoff, invoking recovery workflows, and using JSONPath for complex validations.\n  Components and references: extracting repeated patterns (auth headers, retry logic, pagination inputs) into reusable definitions and referencing them across workflows.\n\n\nWorking with Arazzo\n\n\n  Runtime expressions: the $inputs, $steps, $response, and $statusCode expression syntax that powers data flow between steps. Covers JSON Pointer notation, comparison and logical operators, string interpolation, null safety, and JSONPath for advanced conditions.\n\n\nQuick reference\n\n\n  The Cheat Sheet: a downloadable one-page reference of the entire specification for quick lookup during development."
        },
        {
          "id": "openapi-v3.1-the-perfect-modern-openapi-workflow",
          "title": "The Perfect Modern OpenAPI Workflow",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.1/the-perfect-modern-openapi-workflow/",
          "content": "Modern OpenAPI To the Rescue\n  A Full Workflow with OpenAPI\n  Git-centric Workflow\n  Phase 1: Design &amp; Development    \n      Write OpenAPI\n      API Linting\n      Server-side Validation\n    \n  \n  Phase 2: API Governance    \n      API Design Reviews\n      API Linting Again\n      Contract Testing\n    \n  \n  Phase 3: Deploying Artifacts    \n      Deploy Documentation\n      Deploy Mocks\n      Publish SDKs\n      API Catalog\n      Try it Now\n    \n  \n  Summary\n\n\nFor decades designing and building APIs felt incredibly repetitive, because the\nwhole job seemed to be repeating the “I” in API (Interface) over and over again\nin various formats. Every APIs would define all the endpoints, properties, data\ntypes, values, and validation rules, in all the following places and more:\n\n\n  Reference documentation\n  Request validation\n  Serializers\n  Integration tests\n  Contract tests\n  Postman collections\n  Client libraries\n\n\nThe whole job was just repeating “Yes, the /trips endpoint returns Trips and\nthey have these properties in this format” until you went blue in the face, and\nany effort to automate this was converting infinite tricky formats into other\ntricky formats, with often outdated tooling and a whole lot of duct tape.\n\nModern OpenAPI To the Rescue\n\nThe HTTP API ecosystem has been revolutionized by OpenAPI, and the countless\ntooling vendors who have stepped in to making amazing quality interoperable\ntooling around it. Some people still seem to think OpenAPI is just about API\ndocumentation, but as more and more tooling appeared OpenAPI has clearly defined\nits time and cost savings throughout the API design and development process and\nbeyond.\n\nOpenAPI is a machine readable declaration of the API interface, also known as an\nAPI contract. This single source of truth is helpful the whole way through the\nAPI lifecycle, from design to deployment to deprecation, and if it just so\nhappens that format is really good at outputting as API documentation then\nthat’s not a bad thing.\n\nA Full Workflow with OpenAPI\n\nLots of folks use little bits of OpenAPI tooling in various manual ways, but\nthere is more to be done. Once OpenAPI is placed in version control, and tooling\nis triggered via continuous integration, a truly amazing and powerful workflow\nappears.\n\nHere is the objectives for a truly useful OpenAPI workflow.\n\n\n  One source of truth where API teams update the contract once.\n  API Mocking - Easily spin up fake servers for clients to play around with to\nsee if that API design will hold up before time is wasted coding the wrong\nthing.\n  Automated Style Guides - Make sure APIs being designed/built match chosen\nstandards and conventions on commit or earlier.\n  Beautiful API documentation - Every change should update API documentation\nwithout having to remember to update a CMS or redistribute a PDF.\n  Contract testing using any standard test runner: Jest, PHPUnit/Pest, JUnit,\nRSpec.\n  SDK Generators in popular languages to save every team pretending they know\nhow to code well in all those languages, which can be automatically kept up to\ndate as the API evolves.\n\n\nThis felt like a dream for years, but you can do all of this right now, and it’s\nnot all locked behind one massive expensive walled garden.\n\nGit-centric Workflow\n\nThe vast majority of software is run through some sort of version control, and\nthis is the perfect place to put OpenAPI too: right in there with the source\ncode. This means that any OpenAPI change can go through a review process, and\nonce the actual API implementation has started being built it means the OpenAPI\nand the code should always match, allowing for linting and testing.\n\nOnce the changes are merged to the main branch, it means documentation, mock\nservers, SDKs, and anything else can all be updated along with the main\ndeployment of the source code, so there is no divergence between the code and\nall these other artifacts.\n\n\n\nLet’s take a look at each of these stages of the API lifecycle, and how OpenAPI\ntooling can help.\n\nPhase 1: Design &amp; Development\n\nWrite OpenAPI\n\nCreate OpenAPI with some sort of editor. There are a few graphical\neditors available which make getting\nstarted with OpenAPI considerably easier, but many of them assume you will\ndesign an API, then export it, then never go back to the OpenAPI to update it.\nThey provide awkward importing through web interfaces, or complicated syncing,\nor throw a bunch of confusing paywalls up, so many people prefer to use their\nfavorite editor.\n\nVS Code is a popular choice, with the OpenAPI\nEditor\nextension by 42Crunch providing code navigation, linting, preview, IntelliSense,\nschema enforcement, and a handy snippets feature to avoid repeating things. It\nalso has static and dynamic security analysis of the OpenAPI if you want to make\nsure you’re not making terrible mistakes early on.\n\nAPI Linting\n\nLinting an OpenAPI document is like linting any other source code. Using\nOpenAPI-aware linters, syntax errors and semantic mistakes can be spotted, but\nmore importantly it helps to make sure that the API is of good quality, and the\nOpenAPI that describes it is written properly, right from the start.\n\nLinting can cover anything from enforcing naming conventions for endpoints or\nproperties, outlawing the use of auto-incrementing IDs, picking a single\nversioning scheme across multiple APIs, improving documentation by making sure\nendpoints have descriptions, requiring a specific data format like\nJSON:API or\nHAL, requesting API designers\nstick to standards like RFC 9457: Problem Details for\nAPIs, or anything else you can imagine\na rule being created for.\n\nTools like vacuum power this concept, and can be run locally using a CLI, or in Visual Studio using vacuum-vscode.\n\nServer-side Validation\n\nOnce the API design has been approved and development begins on the API, the\nOpenAPI is already right there in the repository so why not use it to avoid writing out all the same validation rules as code?\n\nSome people have experimented with generating whole codebases from OpenAPI.\nMaybe you’ll have success with that, but folks are often scared off by the\nconfusion of trying to customize generated code before it is generated again\nwith future changes. Some tools allow for “hook in” points that allow for\ncustomization without regenerating, but this can be feel restrictive.\n\nA far more flexible approach comes with OpenAPI-aware HTTP middlewares. These\nmiddlewares are able to look into the OpenAPI document, and compare incoming\nHTTP requests based on the method and endpoint. From there it’s a simple case of\nrejecting the request if the URL parameters, query string parameters, or payload\nbody do not match the expected contract defined in the OpenAPI document.\n\nFor example, APIs built with Rails can use the openapi_first gem to register a middleware, and point it to OpenAPI.\n\n# config/application.rb\n\nrequire_relative \"boot\"\n\nrequire \"rails/all\"\n\nmodule RailsDesignFirst\n  class Application &lt; Rails::Application\n    config.middleware.use OpenapiFirst::Middlewares::RequestValidation, spec: 'api/openapi.yaml'\n  end\nend\n\n\nFrom there, requests will be rejected if they are missing required properties or are otherwise invalid.\n\ncurl -X POST http://localhost:3000/widgets -H \"Content-Type: application/json\" -d '{}'\n\n\n{\n  \"title\": \"Bad Request Body\",\n  \"status\": 400,\n  \"errors\": [\n    {\n      \"message\": \"object at root is missing required properties: name\",\n      \"pointer\": \"\",\n      \"code\": \"required\"\n    }\n  ]\n}\n\n\nIf we try with a valid request now the OpenAPI middleware should let the request through, and the API should respond with a success.\n\ncurl -X POST http://localhost:3000/widgets -H \"Content-Type: application/json\" -d '{\"name\":\"Replicator\"}'\n\n\n{\n  \"id\": 1,\n  \"name\": \"Replicator\",\n  \"created_at\": \"2024-01-08T16:27:14.151Z\",\n  \"updated_at\": \"2024-01-08T16:27:14.151Z\"\n}\n\n\nWithout needing to write any Ruby code at all, your API is rejecting invalid\nrequests, which is not only saving time writing code, but is making sure the\nOpenAPI and code line up perfectly. It’s pretty hard for code and docs to drift\nwhen they’re sharing a single source of truth like this.\n\nLearn more about using OpenAPI to simplify building Ruby on Rails APIs, with a similar guide for Laravel PHP users.\n\nPhase 2: API Governance\n\nAPI Governance is the framework for making great, consistent, reliable, and\nconsumable APIs. Over the last decade, API governance has gone from some vague\nconcept being mentioned at conferences, to being a beautifully solved problem\nwith a plethora of tooling.\n\nWhilst API governance is a massive topic,\na few key parts can be handled with OpenAPI tooling, speeding up and partially\nautomating “API Design Reviews”, simplifying change detection, automation of\n“API style guides”, and creating an API Catalog that can help keep track of all\nthe APIs in a company so they don’t go missing and fall into disrepair.\n\nAPI Design Reviews\n\nJust as pull requests need to pass automated testing as well as being peer\nreviewed, OpenAPI changes need to be reviewed to make sure they are a good idea\nfor the API, consumers, and organization at large. Design reviews make sure the\nAPI is going to be useful not just valid. By making sure the OpenAPI is\nsuggesting a good change, more mistakes will be caught before they make it into\nproduction.\n\nThis can be a very manual and time consuming process without OpenAPI as there is\na lot of code review, and the folks helping with design reviews might have to\nlearn various languages, multiple frameworks, and understand all the intricacies\nof those frameworks less obvious conventions. Moving the review to OpenAPI\nremoves all of that, and helps to democratize and centralize the process and\nhave more folks involved, instead of just those who understand the code.\n\nTo design reviews easier, Bump.sh can show a list of relevant changes between\nthe OpenAPI in the a pull request and the latest deployed document. This helps\nskip staring at infinite lines of YAML/JSON, trying to understand what, if\nanything, meaningfully changed. New properties, changed property validations, or\nbreaking changes like a removed endpoint or new required property.\n\n# .github/workflows/api-changes.yaml\nname: API Changes\npermissions:\n  contents: read\n  pull-requests: write\n\non:\n  pull_request:\n    branches:\n      - main\n\njobs:\n  changes:\n    name: Detect and preview API changes\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Comment pull request with API diff\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;BUMP_DOC_ID&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: api/openapi.yaml\n          command: diff\n\n\nAPI Linting Again\n\nLinting was used in the design and development phase, running locally in the CLI\nor editor, but linting is useful in the governance phase too. By running API\nlinting on pull requests, much of the design review process can be automated.\n\nFor example, instead of wasting human time spotting naming convention issues, or\nmaking somebody want to quit because their entire job is reminding API\ndevelopers “this endpoint does not have a description, please add one”, the\nrobots can do that work instead.\n\nConfiguring the linter to return errors and warnings as annotations on the\nproblematic lines in a pull request makes it even easier, helping developers\nspot the problems with their OpenAPI as quickly as they would any syntax errors\nin their code.\n\n\n\nOrganizations can use this concept to produce their own\nrulesets, which are\nbasically the old idea of an API Style Guide, but instead of writing out a\nmanifesto which nobody will ever read, it can be an automated process. By adding\nnew rules over time as more problems pop up or best practices evolve, it helps\nto iteratively improve not just the API in question, but all future and existing\nAPIs in the organization.\n\nLet’s combine this extra linting job with the previous API change workflow.\n\n# .github/workflows/api-changes.yaml\nname: API Changes\npermissions:\n  contents: read\n  pull-requests: write\n\non:\n  pull_request:\n    branches: [main]\n  push:\n    branches: [main]\n    paths-ignore:\n      - 'README.md'\n      - 'src/**'\n\njobs:\n  changes:\n    name: Detect and preview API changes\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Comment pull request with API diff\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;BUMP_DOC_ID&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: api/openapi.yaml\n          command: diff\n\n  lint:\n    name: API Linting\n    runs-on: ubuntu-latest\n\n    permissions:\n      statuses: write\n      checks: write\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v4\n\n      - name: Install dependencies\n        run: npm ci\n\n      - name: Lint API\n        run: npm exec vacuum report -- --junit openapi.yaml lint-results\n\n      - name: Publish Lint Results\n        if: success() || failure()\n        uses: mikepenz/action-junit-report@v5\n        with:\n          check_name: API Lint Results\n          report_paths: lint-results-*.xml\n\n\nContract Testing\n\nContract testing used to be complicated, with dedicated testing tools running in\nisolation that had no knowledge of what the contract was meant to be until you\ntold it.\n\nHow would a testing tool know what properties were meant to be returned by any\nparticular endpoint in any particular state until it had been programmed in? How\nwould it be aware of changes made in a recent PR unless it was updated once it\nbroke?\n\nHaving OpenAPI in the same repository as the source code means that every single\ncommit carries with it a perfect description of what the API should be, so at\nany point the existing API test suite should be able to use that OpenAPI for\ncomparison against what the code is actually returning.\n\nFor Rails users, this would take the form of the\nopenapi_contracts gem. Once the\nRSpec test runner is aware of where the OpenAPI document resides, a single\nassertion can be added to existing tests to confirm the returned response\nmatches the API description.\n\nrequire \"rails_helper\"\n\nRSpec.describe 'widgets', type: :request do\n  \n  describe \"GET /widgets\" do\n    it 'responds with 200 and matches the doc' do\n      get '/widgets'\n      expect(response).to match_openapi_doc(OPENAPI_DOC)\n    end\n  end\n\nend\n\n\nRequests that were sent can also be validated to confirm both sides of the HTTP interaction.\n\nit do\n  is_expected.to match_openapi_doc(\n\t  OPENAPI_DOC,\n\t  request_body: true\n  ).with_http_status(:created)\nend\n\n\nRunning this contract testing is done whenever the existing integration test suite is run.\n\n# .github/workflows/tests.yaml\nname: Run RSpec tests\non: [push]\njobs:\n  run-rspec-tests:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - name: Set up Ruby\n        uses: ruby/setup-ruby@v1\n        with:\n          # runs 'bundle install' and caches installed gems automatically\n          bundler-cache: true\n      - name: Run tests\n        run: |\n          bundle exec rspec\n\n\n\n  For a more in depth example, see how to contract test in\nRails, or see how to do the same with\nLaravel PHP. If you’d like to\nsee a similar guide for your favorite language/framework please get in\ntouch.\n\n\nIf an API does not have an existing integration test suite this might seem like\na bigger push, but an API without a test suite should absolutely add one.\nWorking with a generic test suite and adding in some OpenAPI assertions can be a\ngreat way to start off a larger test suite. Set up one HTTP request for each API\nendpoint, with basic information, and add other tests for various scenarios over\ntime as bugs are squashed.\n\nAnother option to avoid that is to run contract testing outside of the codebase.\nTo avoid having to train a tool to know what the expected contract is, why not\nuse a tool which already knows what the latest OpenAPI is meant to be at\nanytime: Microcks again!\n\nMicrocks handles contract\ntesting as well as mock\nservers, and it does this by taking a URL to a server for comparison. This could\nbe staging, pre-production, or even production if you’re careful.\n\nIt works by going through all the operations in the OpenAPI document, and uses\nthe examples and schemas defined there to send a request that should work, to an\nAPI instance of your choosing. This could be production if you are brave, or\nsome other staging/testing environment, but the logic is simple:\n\n\n  Send HTTP requests.\n  See if that fails unexpectedly.\n  Receive HTTP response.\n  See if that matches the OpenAPI document.\n\n\nThis could be automated to run at regular intervals, or it could be triggered to\nrun on pull requests and merges to make sure that any and all API or OpenAPI\nchanges agree with each other. GitHub Actions is once again a good way to get\nthis done.\n\n# .github/workflows/contract-testing.yml\nname: API Contract Testing\non: [push]\njobs:\n  contract-testing:\n    name: Deploy API documentation on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - uses: microcks/import-github-action@v1\n        with:\n          specificationFiles: 'api/openapi.yaml:true'\n          microcksURL: 'https://mocks.example.com/api/'\n          keycloakClientId:  ${{ secrets.MICROCKS_SERVICE_ACCOUNT }}\n          keycloakClientSecret:  ${{ secrets.MICROCKS_SERVICE_ACCOUNT_CREDENTIALS }}\n\n      - uses: microcks/test-github-action@v1\n        with:\n          apiNameAndVersion: 'Train Travel API:1.0.0'\n          testEndpoint: 'http://api-testing.example.com'\n          runner: OPEN_API_SCHEMA\n          microcksURL: 'https://mocks.example.com/api/'\n          keycloakClientId:  ${{ secrets.MICROCKS_SERVICE_ACCOUNT }}\n          keycloakClientSecret:  ${{ secrets.MICROCKS_SERVICE_ACCOUNT_CREDENTIALS }}\n          waitFor: '10sec'\n\n\nLearn more about contract testing with Microcks.\n\nPhase 3: Deploying Artifacts\n\nThere are countless artifacts that need to be kept up-to-date with the API as it\nis changed over time, and instead of doing any of this manually we can let\nOpenAPI handle all of it.\n\nDeploy Documentation\n\nUsing OpenAPI is the easiest way to maintain up-to-date documentation, without\nhaving to remember to make manual changes to some wiki/CMS somewhere, or try to\ntime updates with code deployments. Tools like Bump.sh provide hosted API\ndocumentation which can be updated every time the Git repo receives updated\nOpenAPI.\n\nAs soon as a pull request is merged to the main branch, the OpenAPI document\nthat accompanies the source code can be deployed to the Bump.sh documentation,\nkeeping everything in sync.\n\n# .github/workflows/api-docs.yml\nname: Deploy API documentation\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  deploy-openapi:\n    name: Deploy API documentation on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;your-doc-id-or-slug&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: api/openapi.yaml\n\n\nThis keeps the API reference documentation always relevant, and because it’s\nbeen used for contract testing there cannot have been any “drift” between the\nAPI implementation and the OpenAPI describing it, meaning the reference\ndocumentation must be correct.\n\nDeploy Mocks\n\nThere are countless API mocking tools out there, many of which work with OpenAPI\nto save the manual effort of updating them every time an API changes, whether\nthat is throughout design and development phases, or later as the API evolves.\n\nOne such tool is Microcks, a self-hosted mock server with an admin interface and\neasily accessible HTTP endpoints that simulate the API being described in\nOpenAPI. You could log into that admin panel and let it know somebody has\nupdated the OpenAPI every now and then, but why not automate that to save time.\n\nUsing GitHub Actions to handle that update looks a bit like this:\n\n# .github/workflows/api-mocks.yml\nname: Deploy API mocks\non:\n  push:\n    branches:\n      - main\njobs:\n  deploy-mocks:\n    if: ${{ github.event_name == 'push' }}\n    name: Deploy API mocks to Microcks\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - uses: microcks/import-github-action@v1\n        with:\n          specificationFiles: 'api/openapi.yaml:true'\n          microcksURL: 'https://mocks.example.com/api/'\n          keycloakClientId:  ${{ secrets.MICROCKS_SERVICE_ACCOUNT }}\n          keycloakClientSecret:  ${{ secrets.MICROCKS_SERVICE_ACCOUNT_CREDENTIALS }}\n\n\nThis workflow could be combined with other jobs, or it could be left as its own\nworkflow like this for clarity. Either way, whenever a change is made to the\nmain branch the mock servers will be updated and instantly reflect the latest\nOpenAPI.\n\nLearn more about API mocking with Microcks.\n\nPublish SDKs\n\nThe quicker a customer can integrate with your API, the quicker your business\nwill be making money or solving problems. Some users will be happy to integrate\ndirectly with the API, but many prefer the ease of working within the\nprogramming language through Software Development Kits (SDKs).\n\nThese can be a lot of work to build and keep up to date, but OpenAPI allows API\nproviders to automate the creation of code libraries which allow API consumers\nto work in their programming language of choice instead of poking and prodding\nover HTTP directly.\n\nSDK generation tools have been around for a long time, but in the past the best\noffering was a cumbersome Java-based open-source tool where you generally had to\ndevelop your own templates. Modern tooling like\nSpeakeasy and\nStainless allow API\nproviders to point to an OpenAPI document, and produce type-safe SDKs that your\nteam will be proud of. These will handle tricky functionality like OAuth 2,\nretries, pagination, and even allow for adding custom code to the generated\noutput.\n\nGenerating SDKs whenever commits are merged to main allows for continuously\nup-to-date accurate SDKs.\n\n# .github/workflows/sdks.yml\nname: Publish SDKs\npermissions:\n  checks: write\n  contents: write\n  pull-requests: write\n  statuses: write\n  id-token: write\non:\n  push:\n    branches:\n      - main\n    paths:\n      - .speakeasy/gen.lock\n  workflow_dispatch: {}\njobs:\n  publish:\n    uses: speakeasy-api/sdk-generation-action/.github/workflows/sdk-publish.yaml@v15\n    with:\n      target: train-travel-sdk\n    secrets:\n      github_access_token: ${{ secrets.GITHUB_TOKEN }}\n      npm_token: ${{ secrets.NPM_TOKEN }}\n      speakeasy_api_key: ${{ secrets.SPEAKEASY_API_KEY }}\n\n\nSpeakeasy will not only automatically generate these SDKs on every push to the\nmain branch, but will tag versions as appropriate, and with a bit of other\nsetup these SDKs can be pushed directly to package managers like NPM, PyPI,\nPackagist, NuGet, and Maven.\n\nOnce this is done, you can update API documentation on Bump.sh to include these\nSDKs in the code examples, instead of the default of showing curl CLI examples,\nor rudimentary code samples like using fetch() or other low-level HTTP code.\n\n\n\nLearn more about SDK generation with Speakeasy.\n\nAPI Catalog\n\nAnother key part of API governance is discoverability, which usually takes the\nform of “API Catalogs”. Infinite awkward solutions have been employed in the\npast. API teams have bodged together infinite awkward solutions with miles of\nduct-tape, but Bump.sh makes this easy with Bump.sh\nHubs.\n\nEach API that has been added to Bump.sh’s hosted documentation can be grouped\ntogether into various Hubs, which could be for “Public” and “Internal” usage.\nAlternatively, Hubs could be used to group APIs by team or department.\n\n\n\nLearn more about API discovery with Bump.sh Hubs.\n\nTry it Now\n\nSeeing how an API works is the first step in an API consumers journey to using\nthe API, and the second step is making some test requests to get a feel for how\nit works. Some people like to do this with code, so code samples will be a good\nstart for them, especially if you have an SDL. Other people like to do this with\ninteractive HTTP clients, like Postman.\n\nPostman is able to import OpenAPI and create an API collection from it. You can\nmanually do this step via the Postman interface, then pop a Run in\nPostman\nbutton into the Bump.sh API documentation to help API consumers find and use that\ncollection in their Postman application.\n\nKeeping it up-to-date as the API evolves via Postman’s Git\nsyncing\ncan be a lot more involved, and require an more expensive plan, but you can\ncobble a solution together using the Postman Pro API if you are feeling up for\nit.\n\nTo avoid spending the time or money, you can let API consumers experiment with\nan API directly from the Bump.sh API documentation using the built-in API\nExplorer.\n\n\n\nWhatever you use for an API client, during the design phase when you have no real API to work with, you can point the API client to the mock server so people can still experiment with the API. Then when a sandbox or production server is available you can add that too.\n\nservers:\n  - url: https://try.microcks.io/rest/Train+Travel+API/1.0.0\n    description: Mock Server\n\n  - url: https://api.example.com\n    description: Production\n\n\nSummary\n\nThe API Design first workflow is often considered to be “more work”, but it’s\nalways inherently less work than the alternatives.\n\nBy investing the time to make OpenAPI early, treating it like code, then using\nit as a source of truth to reduce how much code, documentation, mocking,\ntesting, and SDK generation the organization needs to throw manhours at, you can\ndrastically improve the speed of API development. API governance becomes a much\neasier reality, and costly mistakes can be averted when avoidable production\nchanges force clients to waste time changing things in their production\nenvironments.\n\nInstead of throwing huge sums of money at one single SaaS platform which\npromises to handle every single step, you can piece together your own perfect\nworkflow, with tooling that you control and the only centralization being your\nsource code and your continuous integration of choice, be that GitHub Actions or\nanything else."
        },
        {
          "id": "openapi-v3.2-the-perfect-modern-openapi-workflow",
          "title": "The Perfect Modern OpenAPI Workflow",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/the-perfect-modern-openapi-workflow/",
          "content": "Modern OpenAPI To the Rescue\n  A Full Workflow with OpenAPI\n  Git-centric Workflow\n  Phase 1: Design &amp; Development    \n      Write OpenAPI\n      API Linting\n      Server-side Validation\n    \n  \n  Phase 2: API Governance    \n      API Design Reviews\n      API Linting Again\n      Contract Testing\n    \n  \n  Phase 3: Deploying Artifacts    \n      Deploy Documentation\n      Deploy Mocks\n      Publish SDKs\n      API Catalog\n      Try it Now\n    \n  \n  Summary\n\n\nFor decades designing and building APIs felt incredibly repetitive, because the\nwhole job seemed to be repeating the “I” in API (Interface) over and over again\nin various formats. Every APIs would define all the endpoints, properties, data\ntypes, values, and validation rules, in all the following places and more:\n\n\n  Reference documentation\n  Request validation\n  Serializers\n  Integration tests\n  Contract tests\n  Postman collections\n  Client libraries\n\n\nThe whole job was just repeating “Yes, the /trips endpoint returns Trips and\nthey have these properties in this format” until you went blue in the face, and\nany effort to automate this was converting infinite tricky formats into other\ntricky formats, with often outdated tooling and a whole lot of duct tape.\n\nModern OpenAPI To the Rescue\n\nThe HTTP API ecosystem has been revolutionized by OpenAPI, and the countless\ntooling vendors who have stepped in to making amazing quality interoperable\ntooling around it. Some people still seem to think OpenAPI is just about API\ndocumentation, but as more and more tooling appeared OpenAPI has clearly defined\nits time and cost savings throughout the API design and development process and\nbeyond.\n\nOpenAPI is a machine readable declaration of the API interface, also known as an\nAPI contract. This single source of truth is helpful the whole way through the\nAPI lifecycle, from design to deployment to deprecation, and if it just so\nhappens that format is really good at outputting as API documentation then\nthat’s not a bad thing.\n\nA Full Workflow with OpenAPI\n\nLots of folks use little bits of OpenAPI tooling in various manual ways, but\nthere is more to be done. Once OpenAPI is placed in version control, and tooling\nis triggered via continuous integration, a truly amazing and powerful workflow\nappears.\n\nHere is the objectives for a truly useful OpenAPI workflow.\n\n\n  One source of truth where API teams update the contract once.\n  API Mocking - Easily spin up fake servers for clients to play around with to\nsee if that API design will hold up before time is wasted coding the wrong\nthing.\n  Automated Style Guides - Make sure APIs being designed/built match chosen\nstandards and conventions on commit or earlier.\n  Beautiful API documentation - Every change should update API documentation\nwithout having to remember to update a CMS or redistribute a PDF.\n  Contract testing using any standard test runner: Jest, PHPUnit/Pest, JUnit,\nRSpec.\n  SDK Generators in popular languages to save every team pretending they know\nhow to code well in all those languages, which can be automatically kept up to\ndate as the API evolves.\n\n\nThis felt like a dream for years, but you can do all of this right now, and it’s\nnot all locked behind one massive expensive walled garden.\n\nGit-centric Workflow\n\nThe vast majority of software is run through some sort of version control, and\nthis is the perfect place to put OpenAPI too: right in there with the source\ncode. This means that any OpenAPI change can go through a review process, and\nonce the actual API implementation has started being built it means the OpenAPI\nand the code should always match, allowing for linting and testing.\n\nOnce the changes are merged to the main branch, it means documentation, mock\nservers, SDKs, and anything else can all be updated along with the main\ndeployment of the source code, so there is no divergence between the code and\nall these other artifacts.\n\n\n\nLet’s take a look at each of these stages of the API lifecycle, and how OpenAPI\ntooling can help.\n\nPhase 1: Design &amp; Development\n\nWrite OpenAPI\n\nCreate OpenAPI with some sort of editor. There are a few graphical\neditors available which make getting\nstarted with OpenAPI considerably easier, but many of them assume you will\ndesign an API, then export it, then never go back to the OpenAPI to update it.\nThey provide awkward importing through web interfaces, or complicated syncing,\nor throw a bunch of confusing paywalls up, so many people prefer to use their\nfavorite editor.\n\nVS Code is a popular choice, with the OpenAPI\nEditor\nextension by 42Crunch providing code navigation, linting, preview, IntelliSense,\nschema enforcement, and a handy snippets feature to avoid repeating things. It\nalso has static and dynamic security analysis of the OpenAPI if you want to make\nsure you’re not making terrible mistakes early on.\n\nAPI Linting\n\nLinting an OpenAPI document is like linting any other source code. Using\nOpenAPI-aware linters, syntax errors and semantic mistakes can be spotted, but\nmore importantly it helps to make sure that the API is of good quality, and the\nOpenAPI that describes it is written properly, right from the start.\n\nLinting can cover anything from enforcing naming conventions for endpoints or\nproperties, outlawing the use of auto-incrementing IDs, picking a single\nversioning scheme across multiple APIs, improving documentation by making sure\nendpoints have descriptions, requiring a specific data format like\nJSON:API or\nHAL, requesting API designers\nstick to standards like RFC 9457: Problem Details for\nAPIs, or anything else you can imagine\na rule being created for.\n\nTools like vacuum power this concept, and can be run locally using a CLI, or in Visual Studio using vacuum-vscode.\n\nServer-side Validation\n\nOnce the API design has been approved and development begins on the API, the\nOpenAPI is already right there in the repository so why not use it to avoid writing out all the same validation rules as code?\n\nSome people have experimented with generating whole codebases from OpenAPI.\nMaybe you’ll have success with that, but folks are often scared off by the\nconfusion of trying to customize generated code before it is generated again\nwith future changes. Some tools allow for “hook in” points that allow for\ncustomization without regenerating, but this can be feel restrictive.\n\nA far more flexible approach comes with OpenAPI-aware HTTP middlewares. These\nmiddlewares are able to look into the OpenAPI document, and compare incoming\nHTTP requests based on the method and endpoint. From there it’s a simple case of\nrejecting the request if the URL parameters, query string parameters, or payload\nbody do not match the expected contract defined in the OpenAPI document.\n\nFor example, APIs built with Rails can use the openapi_first gem to register a middleware, and point it to OpenAPI.\n\n# config/application.rb\n\nrequire_relative \"boot\"\n\nrequire \"rails/all\"\n\nmodule RailsDesignFirst\n  class Application &lt; Rails::Application\n    config.middleware.use OpenapiFirst::Middlewares::RequestValidation, spec: 'api/openapi.yaml'\n  end\nend\n\n\nFrom there, requests will be rejected if they are missing required properties or are otherwise invalid.\n\ncurl -X POST http://localhost:3000/widgets -H \"Content-Type: application/json\" -d '{}'\n\n\n{\n  \"title\": \"Bad Request Body\",\n  \"status\": 400,\n  \"errors\": [\n    {\n      \"message\": \"object at root is missing required properties: name\",\n      \"pointer\": \"\",\n      \"code\": \"required\"\n    }\n  ]\n}\n\n\nIf we try with a valid request now the OpenAPI middleware should let the request through, and the API should respond with a success.\n\ncurl -X POST http://localhost:3000/widgets -H \"Content-Type: application/json\" -d '{\"name\":\"Replicator\"}'\n\n\n{\n  \"id\": 1,\n  \"name\": \"Replicator\",\n  \"created_at\": \"2024-01-08T16:27:14.151Z\",\n  \"updated_at\": \"2024-01-08T16:27:14.151Z\"\n}\n\n\nWithout needing to write any Ruby code at all, your API is rejecting invalid\nrequests, which is not only saving time writing code, but is making sure the\nOpenAPI and code line up perfectly. It’s pretty hard for code and docs to drift\nwhen they’re sharing a single source of truth like this.\n\nLearn more about using OpenAPI to simplify building Ruby on Rails APIs, with a similar guide for Laravel PHP users.\n\nPhase 2: API Governance\n\nAPI Governance is the framework for making great, consistent, reliable, and\nconsumable APIs. Over the last decade, API governance has gone from some vague\nconcept being mentioned at conferences, to being a beautifully solved problem\nwith a plethora of tooling.\n\nWhilst API governance is a massive topic,\na few key parts can be handled with OpenAPI tooling, speeding up and partially\nautomating “API Design Reviews”, simplifying change detection, automation of\n“API style guides”, and creating an API Catalog that can help keep track of all\nthe APIs in a company so they don’t go missing and fall into disrepair.\n\nAPI Design Reviews\n\nJust as pull requests need to pass automated testing as well as being peer\nreviewed, OpenAPI changes need to be reviewed to make sure they are a good idea\nfor the API, consumers, and organization at large. Design reviews make sure the\nAPI is going to be useful not just valid. By making sure the OpenAPI is\nsuggesting a good change, more mistakes will be caught before they make it into\nproduction.\n\nThis can be a very manual and time consuming process without OpenAPI as there is\na lot of code review, and the folks helping with design reviews might have to\nlearn various languages, multiple frameworks, and understand all the intricacies\nof those frameworks less obvious conventions. Moving the review to OpenAPI\nremoves all of that, and helps to democratize and centralize the process and\nhave more folks involved, instead of just those who understand the code.\n\nTo design reviews easier, Bump.sh can show a list of relevant changes between\nthe OpenAPI in the a pull request and the latest deployed document. This helps\nskip staring at infinite lines of YAML/JSON, trying to understand what, if\nanything, meaningfully changed. New properties, changed property validations, or\nbreaking changes like a removed endpoint or new required property.\n\n# .github/workflows/api-changes.yaml\nname: API Changes\npermissions:\n  contents: read\n  pull-requests: write\n\non:\n  pull_request:\n    branches:\n      - main\n\njobs:\n  changes:\n    name: Detect and preview API changes\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Comment pull request with API diff\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;BUMP_DOC_ID&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: api/openapi.yaml\n          command: diff\n\n\nAPI Linting Again\n\nLinting was used in the design and development phase, running locally in the CLI\nor editor, but linting is useful in the governance phase too. By running API\nlinting on pull requests, much of the design review process can be automated.\n\nFor example, instead of wasting human time spotting naming convention issues, or\nmaking somebody want to quit because their entire job is reminding API\ndevelopers “this endpoint does not have a description, please add one”, the\nrobots can do that work instead.\n\nConfiguring the linter to return errors and warnings as annotations on the\nproblematic lines in a pull request makes it even easier, helping developers\nspot the problems with their OpenAPI as quickly as they would any syntax errors\nin their code.\n\n\n\nOrganizations can use this concept to produce their own\nrulesets, which are\nbasically the old idea of an API Style Guide, but instead of writing out a\nmanifesto which nobody will ever read, it can be an automated process. By adding\nnew rules over time as more problems pop up or best practices evolve, it helps\nto iteratively improve not just the API in question, but all future and existing\nAPIs in the organization.\n\nLet’s combine this extra linting job with the previous API change workflow.\n\n# .github/workflows/api-changes.yaml\nname: API Changes\npermissions:\n  contents: read\n  pull-requests: write\n\non:\n  pull_request:\n    branches: [main]\n  push:\n    branches: [main]\n    paths-ignore:\n      - 'README.md'\n      - 'src/**'\n\njobs:\n  changes:\n    name: Detect and preview API changes\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Comment pull request with API diff\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;BUMP_DOC_ID&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: api/openapi.yaml\n          command: diff\n\n  lint:\n    name: API Linting\n    runs-on: ubuntu-latest\n\n    permissions:\n      statuses: write\n      checks: write\n\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v4\n\n      - name: Install dependencies\n        run: npm ci\n\n      - name: Lint API\n        run: npm exec vacuum report -- --junit openapi.yaml lint-results\n\n      - name: Publish Lint Results\n        if: success() || failure()\n        uses: mikepenz/action-junit-report@v5\n        with:\n          check_name: API Lint Results\n          report_paths: lint-results-*.xml\n\n\nContract Testing\n\nContract testing used to be complicated, with dedicated testing tools running in\nisolation that had no knowledge of what the contract was meant to be until you\ntold it.\n\nHow would a testing tool know what properties were meant to be returned by any\nparticular endpoint in any particular state until it had been programmed in? How\nwould it be aware of changes made in a recent PR unless it was updated once it\nbroke?\n\nHaving OpenAPI in the same repository as the source code means that every single\ncommit carries with it a perfect description of what the API should be, so at\nany point the existing API test suite should be able to use that OpenAPI for\ncomparison against what the code is actually returning.\n\nFor Rails users, this would take the form of the\nopenapi_contracts gem. Once the\nRSpec test runner is aware of where the OpenAPI document resides, a single\nassertion can be added to existing tests to confirm the returned response\nmatches the API description.\n\nrequire \"rails_helper\"\n\nRSpec.describe 'widgets', type: :request do\n  \n  describe \"GET /widgets\" do\n    it 'responds with 200 and matches the doc' do\n      get '/widgets'\n      expect(response).to match_openapi_doc(OPENAPI_DOC)\n    end\n  end\n\nend\n\n\nRequests that were sent can also be validated to confirm both sides of the HTTP interaction.\n\nit do\n  is_expected.to match_openapi_doc(\n\t  OPENAPI_DOC,\n\t  request_body: true\n  ).with_http_status(:created)\nend\n\n\nRunning this contract testing is done whenever the existing integration test suite is run.\n\n# .github/workflows/tests.yaml\nname: Run RSpec tests\non: [push]\njobs:\n  run-rspec-tests:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - name: Set up Ruby\n        uses: ruby/setup-ruby@v1\n        with:\n          # runs 'bundle install' and caches installed gems automatically\n          bundler-cache: true\n      - name: Run tests\n        run: |\n          bundle exec rspec\n\n\n\n  For a more in depth example, see how to contract test in\nRails, or see how to do the same with\nLaravel PHP. If you’d like to\nsee a similar guide for your favorite language/framework please get in\ntouch.\n\n\nIf an API does not have an existing integration test suite this might seem like\na bigger push, but an API without a test suite should absolutely add one.\nWorking with a generic test suite and adding in some OpenAPI assertions can be a\ngreat way to start off a larger test suite. Set up one HTTP request for each API\nendpoint, with basic information, and add other tests for various scenarios over\ntime as bugs are squashed.\n\nAnother option to avoid that is to run contract testing outside of the codebase.\nTo avoid having to train a tool to know what the expected contract is, why not\nuse a tool which already knows what the latest OpenAPI is meant to be at\nanytime: Microcks again!\n\nMicrocks handles contract\ntesting as well as mock\nservers, and it does this by taking a URL to a server for comparison. This could\nbe staging, pre-production, or even production if you’re careful.\n\nIt works by going through all the operations in the OpenAPI document, and uses\nthe examples and schemas defined there to send a request that should work, to an\nAPI instance of your choosing. This could be production if you are brave, or\nsome other staging/testing environment, but the logic is simple:\n\n\n  Send HTTP requests.\n  See if that fails unexpectedly.\n  Receive HTTP response.\n  See if that matches the OpenAPI document.\n\n\nThis could be automated to run at regular intervals, or it could be triggered to\nrun on pull requests and merges to make sure that any and all API or OpenAPI\nchanges agree with each other. GitHub Actions is once again a good way to get\nthis done.\n\n# .github/workflows/contract-testing.yml\nname: API Contract Testing\non: [push]\njobs:\n  contract-testing:\n    name: Deploy API documentation on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - uses: microcks/import-github-action@v1\n        with:\n          specificationFiles: 'api/openapi.yaml:true'\n          microcksURL: 'https://mocks.example.com/api/'\n          keycloakClientId:  ${{ secrets.MICROCKS_SERVICE_ACCOUNT }}\n          keycloakClientSecret:  ${{ secrets.MICROCKS_SERVICE_ACCOUNT_CREDENTIALS }}\n\n      - uses: microcks/test-github-action@v1\n        with:\n          apiNameAndVersion: 'Train Travel API:1.0.0'\n          testEndpoint: 'http://api-testing.example.com'\n          runner: OPEN_API_SCHEMA\n          microcksURL: 'https://mocks.example.com/api/'\n          keycloakClientId:  ${{ secrets.MICROCKS_SERVICE_ACCOUNT }}\n          keycloakClientSecret:  ${{ secrets.MICROCKS_SERVICE_ACCOUNT_CREDENTIALS }}\n          waitFor: '10sec'\n\n\nLearn more about contract testing with Microcks.\n\nPhase 3: Deploying Artifacts\n\nThere are countless artifacts that need to be kept up-to-date with the API as it\nis changed over time, and instead of doing any of this manually we can let\nOpenAPI handle all of it.\n\nDeploy Documentation\n\nUsing OpenAPI is the easiest way to maintain up-to-date documentation, without\nhaving to remember to make manual changes to some wiki/CMS somewhere, or try to\ntime updates with code deployments. Tools like Bump.sh provide hosted API\ndocumentation which can be updated every time the Git repo receives updated\nOpenAPI.\n\nAs soon as a pull request is merged to the main branch, the OpenAPI document\nthat accompanies the source code can be deployed to the Bump.sh documentation,\nkeeping everything in sync.\n\n# .github/workflows/api-docs.yml\nname: Deploy API documentation\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  deploy-openapi:\n    name: Deploy API documentation on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: &lt;your-doc-id-or-slug&gt;\n          token: ${{secrets.BUMP_TOKEN}}\n          file: api/openapi.yaml\n\n\nThis keeps the API reference documentation always relevant, and because it’s\nbeen used for contract testing there cannot have been any “drift” between the\nAPI implementation and the OpenAPI describing it, meaning the reference\ndocumentation must be correct.\n\nDeploy Mocks\n\nThere are countless API mocking tools out there, many of which work with OpenAPI\nto save the manual effort of updating them every time an API changes, whether\nthat is throughout design and development phases, or later as the API evolves.\n\nOne such tool is Microcks, a self-hosted mock server with an admin interface and\neasily accessible HTTP endpoints that simulate the API being described in\nOpenAPI. You could log into that admin panel and let it know somebody has\nupdated the OpenAPI every now and then, but why not automate that to save time.\n\nUsing GitHub Actions to handle that update looks a bit like this:\n\n# .github/workflows/api-mocks.yml\nname: Deploy API mocks\non:\n  push:\n    branches:\n      - main\njobs:\n  deploy-mocks:\n    if: ${{ github.event_name == 'push' }}\n    name: Deploy API mocks to Microcks\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - uses: microcks/import-github-action@v1\n        with:\n          specificationFiles: 'api/openapi.yaml:true'\n          microcksURL: 'https://mocks.example.com/api/'\n          keycloakClientId:  ${{ secrets.MICROCKS_SERVICE_ACCOUNT }}\n          keycloakClientSecret:  ${{ secrets.MICROCKS_SERVICE_ACCOUNT_CREDENTIALS }}\n\n\nThis workflow could be combined with other jobs, or it could be left as its own\nworkflow like this for clarity. Either way, whenever a change is made to the\nmain branch the mock servers will be updated and instantly reflect the latest\nOpenAPI.\n\nLearn more about API mocking with Microcks.\n\nPublish SDKs\n\nThe quicker a customer can integrate with your API, the quicker your business\nwill be making money or solving problems. Some users will be happy to integrate\ndirectly with the API, but many prefer the ease of working within the\nprogramming language through Software Development Kits (SDKs).\n\nThese can be a lot of work to build and keep up to date, but OpenAPI allows API\nproviders to automate the creation of code libraries which allow API consumers\nto work in their programming language of choice instead of poking and prodding\nover HTTP directly.\n\nSDK generation tools have been around for a long time, but in the past the best\noffering was a cumbersome Java-based open-source tool where you generally had to\ndevelop your own templates. Modern tooling like\nSpeakeasy and\nStainless allow API\nproviders to point to an OpenAPI document, and produce type-safe SDKs that your\nteam will be proud of. These will handle tricky functionality like OAuth 2,\nretries, pagination, and even allow for adding custom code to the generated\noutput.\n\nGenerating SDKs whenever commits are merged to main allows for continuously\nup-to-date accurate SDKs.\n\n# .github/workflows/sdks.yml\nname: Publish SDKs\npermissions:\n  checks: write\n  contents: write\n  pull-requests: write\n  statuses: write\n  id-token: write\non:\n  push:\n    branches:\n      - main\n    paths:\n      - .speakeasy/gen.lock\n  workflow_dispatch: {}\njobs:\n  publish:\n    uses: speakeasy-api/sdk-generation-action/.github/workflows/sdk-publish.yaml@v15\n    with:\n      target: train-travel-sdk\n    secrets:\n      github_access_token: ${{ secrets.GITHUB_TOKEN }}\n      npm_token: ${{ secrets.NPM_TOKEN }}\n      speakeasy_api_key: ${{ secrets.SPEAKEASY_API_KEY }}\n\n\nSpeakeasy will not only automatically generate these SDKs on every push to the\nmain branch, but will tag versions as appropriate, and with a bit of other\nsetup these SDKs can be pushed directly to package managers like NPM, PyPI,\nPackagist, NuGet, and Maven.\n\nOnce this is done, you can update API documentation on Bump.sh to include these\nSDKs in the code examples, instead of the default of showing curl CLI examples,\nor rudimentary code samples like using fetch() or other low-level HTTP code.\n\n\n\nLearn more about SDK generation with Speakeasy.\n\nAPI Catalog\n\nAnother key part of API governance is discoverability, which usually takes the\nform of “API Catalogs”. Infinite awkward solutions have been employed in the\npast. API teams have bodged together infinite awkward solutions with miles of\nduct-tape, but Bump.sh makes this easy with Bump.sh\nHubs.\n\nEach API that has been added to Bump.sh’s hosted documentation can be grouped\ntogether into various Hubs, which could be for “Public” and “Internal” usage.\nAlternatively, Hubs could be used to group APIs by team or department.\n\n\n\nLearn more about API discovery with Bump.sh Hubs.\n\nTry it Now\n\nSeeing how an API works is the first step in an API consumers journey to using\nthe API, and the second step is making some test requests to get a feel for how\nit works. Some people like to do this with code, so code samples will be a good\nstart for them, especially if you have an SDL. Other people like to do this with\ninteractive HTTP clients, like Postman.\n\nPostman is able to import OpenAPI and create an API collection from it. You can\nmanually do this step via the Postman interface, then pop a Run in\nPostman\nbutton into the Bump.sh API documentation to help API consumers find and use that\ncollection in their Postman application.\n\nKeeping it up-to-date as the API evolves via Postman’s Git\nsyncing\ncan be a lot more involved, and require an more expensive plan, but you can\ncobble a solution together using the Postman Pro API if you are feeling up for\nit.\n\nTo avoid spending the time or money, you can let API consumers experiment with\nan API directly from the Bump.sh API documentation using the built-in API\nExplorer.\n\n\n\nWhatever you use for an API client, during the design phase when you have no real API to work with, you can point the API client to the mock server so people can still experiment with the API. Then when a sandbox or production server is available you can add that too.\n\nservers:\n  - name: Mock Server\n    url: https://try.microcks.io/rest/Train+Travel+API/1.0.0\n    description: &gt;\n      This is a mock server provided by Microcks, which allows you to try out the API without needing to set up your own server.\n\n  - name: Production\n    url: https://api.example.com\n    description: &gt;\n      This is the production server for the Train Travel API, which is used to book train tickets and check schedules.\n\n\nSummary\n\nThe API Design first workflow is often considered to be “more work”, but it’s\nalways inherently less work than the alternatives.\n\nBy investing the time to make OpenAPI early, treating it like code, then using\nit as a source of truth to reduce how much code, documentation, mocking,\ntesting, and SDK generation the organization needs to throw manhours at, you can\ndrastically improve the speed of API development. API governance becomes a much\neasier reality, and costly mistakes can be averted when avoidable production\nchanges force clients to waste time changing things in their production\nenvironments.\n\nInstead of throwing huge sums of money at one single SaaS platform which\npromises to handle every single step, you can piece together your own perfect\nworkflow, with tooling that you control and the only centralization being your\nsource code and your continuous integration of choice, be that GitHub Actions or\nanything else."
        },
        {
          "id": "openapi-v3.2-understanding-structure-http-responses",
          "title": "HTTP Responses",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/understanding-structure/http-responses/",
          "content": "HTTP Status Codes    \n      Status Ranges\n      More about HTTP Status Codes\n    \n  \n  Empty status body\n\n\nHTTP responses outline what an API user could expect to receive in response to a HTTP request. In OpenAPI responses described in the responses object, broken down by expected media-types and status codes.\n\npaths:\n  /health:\n    get:\n      responses:\n        '200':\n          description: OK\n          content:\n            application/json:\n              schema:\n                type: object\n                properties:\n                  healthy:\n                    type: boolean  \n\n\nHere’s an example from the Train Travel API, showing two responses for the same operation, one success and one failure, both defining a JSON response:\n\n  responses:\n\n    '200':\n      description: A list of train stations\n      headers:\n        RateLimit:\n          description: The RateLimit header communicates quota policies.\n          schema:\n            type: string\n            examples:\n              - limit=10, remaining=0, reset=10\n      content:\n        application/json:\n          schema:\n            properties:\n              $ref: '#/components/schemas/Stations'\n\n    '400':\n      description: Bad Request\n      content:\n        application/problem+json:\n          schema:\n            $ref: '#/components/schemas/Problem'\n\n\n\n  These responses contain shared schemas which are referenced via the components section to keep the relevant parts of the example clear, but you can learn more about schemas to see what else could go in there.\n\n\nThe key parts that define a response:\n\ndescription: A short, optional, descriptive text about the response. It explains the meaning of the response in the context of the API operation. This is often just the status code text, so 200 would be “Ok”, 201 would be “Success”, but it can be anything you think makes sense.\n\nheaders: An optional map of headers that can be sent by the response. Each header is itself described by an object, which defines the name as a key, then has an object with a description of its own and a schema to describe the header. then Cache-Controls, RFC headers like RateLimit or custom headers like 'X-Rate-Limit'.\n\ncontent: An optional field that describes the content of the response body. It allows for different media types to be documented, specifying how the body of the response should be formatted. For each media type, you can define a schema and examples, making it clear what the response will look like.\n\nlinks: An optional section that can define hypermedia relations associated with the response. Links can show clients what operations might be related or available to them after receiving the response, essentially guiding them on what they can do next.\n\nThe HTTP response object in OpenAPI allows for detailed documentation of each possible outcome of an API operation, making it easier for developers to understand and handle those responses correctly in their applications.\n\nHTTP Status Codes\n\nHTTP status codes are essential for defining the responses of API operations. Each response in an API operation must include at least one HTTP status code, such as 200 for success or 404 for not found. Typically an operation specifies one successful status code for the “happy path”, and one or more error statuses describing the variety of things that can go wrong.\n\n  responses:\n    '200':\n      description: OK\n    '304':\n      description: Not Modified\n    '400':\n      description: Bad Request\n    '401':\n      description: Unauthorized\n    '403':\n      description: Forbidden\n    '429':\n      description: Too Many Requests\n    '500':\n      description: Internal Server Error\n\n\nHow many status codes you choose to describe is up to you. There is a balance to be found between “only the status codes the API is programmed to emit” and “everything that could possibly ever come out of the API, server, and network components involved” which is going to be different for everyone.\n\nStatus Ranges\n\nOpenAPI allows defining a range of response codes to simplify documentation:\n\n\n  1XX for informational responses\n  2XX for successful responses\n  3XX for redirection messages\n  4XX for client errors\n  5XX for server errors\n\n\nIf a specific code is detailed within a range, that code’s definition takes precedence over the general range definition. Each status code in the documentation requires a description, explaining under what conditions it is returned. Markdown (CommonMark) can be used for these descriptions to include rich text formatting.\n\nMore about HTTP Status Codes\n\nFor more detailed information on HTTP status codes, the OpenAPI Specification defers to RFC 9110 and the IANA Status Code Registry. If a code is defined there, it’s valid to use in your OpenAPI.\n\nIf you’re struggling to remember which HTTP status codes to use for any scenario, HTTP Cats will help you visualize the right choice.\n\nEmpty status body\n\nSome HTTP responses do not have a response body.\n\nFor example, 204 No Content is often used after something has been deleted and therefore there is nothing to return.\n\npaths:\n  /example:\n    get:\n      summary: \"Endpoint with no response body\"\n      responses:\n        '204':\n          description: \"No content to return\"\n          # No 'content' field here\n\n\nAnother common one is 304 Not Modified, which lets clients know they can reuse previous cached responses because nothing has changed on the server.\n\nTo describe HTTP responses with no body in OpenAPI you simply leave the content object out.\n\n\n  Before OpenAPI v3.2 this behavior was unclear. Missing out the content object could have meant not response, or it could have meant that it was simply undocumented. OpenAPI v3.2 clarified that no content means no response body should be returned."
        },
        {
          "id": "openapi-v3.2-documentation-descriptions-and-summaries",
          "title": "Descriptions and Summaries",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/documentation/descriptions-and-summaries/",
          "content": "Example of Incomplete Documentation\n  Parameter Descriptions\n  Adding Descriptions to Tags\n  Operations\n  Responses\n\n\nOften, API documentation is left to the end of the schedule, which can lead to a rushed set of API reference documentation. This results in terse outlines of what endpoints and parameters exist, but leave out the specifics of your well-designed and implemented API operation. One of the major ways to improve the developer experience is to upgrade your OpenAPI from being a quick list of parameters into a store of import context and human knowledge with expanding descriptions and summaries.\n\nExample of Incomplete Documentation\n\nFor example, an operation might be described simply as GET /users with no further explanation, leaving users to guess what the endpoint does.\n\nExample Mistake:\n\npaths:\n  /bookings:\n    get:\n      summary: Get bookings\n      parameters:\n        - in: query\n          name: offset\n          schema:\n            type: integer\n            default: 10\n      responses:\n        '200':\n          description: OK\n\n\nThis is barely useful. The summary is repeating “Get bookings” like thats not clear from the “get” and the “/bookings” which would be displayed in most documentation tools by default. The summary is a chance to add more human information, like an alt tag or a caption, for things that aren’t already there.\n\nDetailed and clear descriptions enhance understanding and usability. The improved version should thoroughly explain the operation, including its purpose, parameters, and response types.\n\nImproved Example:\n\npaths:\n  /bookings:\n    get:\n      summary: List bookings for user\n      description: Returns a paginated list of trip bookings by the authenticated user.\n      parameters:\n        - in: query\n          name: offset\n          schema:\n            type: integer\n            default: 10\n          description: &gt; \n            Used for pagination, the offset parameter allows you to skip \n            through the dataset to load the next set of records.\n      responses:\n        '200':\n          description: A paginated list of bookings with detailed information.\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Bookings'\n\n\nLonger is not always better, and efforts to programmatically enforce the use of descriptions can lead to combative entries like “The User ID parameter contains an ID for a User” which no client wants to see. If you put some effort into building a culture around respecting the need for sharing context with your users, you can see quicker and more successful uptake of your API en masse. This has the handy benefit of reducing demands on the support teams as they have fewer confused users to help.\n\nThis example showed a few types of description, but you can put descriptions and summaries in quite a few places. Let’s take a look.\n\nParameter Descriptions\n\nParameters often seem really obvious to the author but not so much when somebody is quickly trying to integrate a single endpoint. For example, here origin and destination could be anything. Coordinates? City names? Station Codes? Station IDs?\n\n  /trips:\n    get:\n      parameters:\n        - name: origin\n          in: query\n          description: The ID of the origin station.\n          required: true\n          schema:\n            type: string\n            format: uuid\n        - name: destination\n          in: query\n          description: The ID of the destination station.\n          required: true\n          schema:\n            type: string\n            format: uuid\n\n\nHere the descriptions make that perfectly clear, regardless of which API documentation tool ends up being used to render this information.\n\nDescriptions are also a handy place to be more specific with criteria for the data that may have been vague otherwise.\n\n  /trips:\n    get:\n      parameters:\n      - name: date\n        in: query\n        description: The date and time of the trip in ISO 8601 format in origin station's timezone.\n        required: true\n        schema:\n          type: string\n          format: date-time\n          examples: ['2024-02-01T09:00:00Z']\n\n\nHere we have a format: date-time which should be displayed to users in API documentation and used in various other tools, and we have an example which shows what format it should look like, but if a user is then trying to configure their own code to use the right date format. If a client had to base it entirely off the example there is lots of room for error, but by clearly stating ISO 8601 in the description, a JavaScript user knows they could use dateObj.toISOString() instead of trying to make some other awkward format.\n\nAdding Descriptions to Tags\n\nTags are often underutilized, but they are a great way to group operations for more structured navigation in most API documentation tools. These tags are also a great place to write longer form context on what these tags actually mean in your domain specifically. For example, the word “Account” can mean 10 different things inside a large organization depending on the department and the function, so it’s good to be super clear about what they are.\n\ntags:\n  - name: stations\n    description: | \n      Find and filter train stations across Europe, including their location\n      and local timezone.\n  - name: trips\n    description: | \n      Timetables and routes for train trips between stations, including pricing\n      and availability.\n  - name: bookings\n    description: | \n      Create and manage bookings for train trips, including passenger details\n      and optional extras.\n  - name: payments\n    description: |\n      Pay for bookings using a card or bank account, and view payment\n      status and history.\n\n      &gt; warn\n      &gt; Bookings usually expire within 1 hour so you'll need to make your payment\n      &gt; before the expiry date \n\n\nHere not only are we explaining the words, but we are helping people find where particular information lives, and providing important context, like that Bookings usually expire within 1 hour which would not have been known to the client otherwise.\n\nOperations\n\nAPIs are rarely as simple as the CRUD (“Create, Read, Update, Delete”) lense many API developers naturally try and view them through. They can sometimes start out that way, but keep these things in mind:\n\n\n  Is the operation returning all records, or restricting data based on the authenticated user.\n  Is the operation using pagination or not.\n  Is there a default status being applied like status=active, and you need provide some other flag to change or remove the default.\n\n\nOnce you get into it, there’s usually quite a lot to say about an operation beyond “Get the Thing”.\n\n  /trips:\n    get:\n      summary: Get available train trips\n      description: Returns a list of available train trips between the specified origin and destination stations on the given date, and allows for filtering by bicycle and dog allowances.\n      parameters:\n        - name: origin\n          in: query\n          description: The ID of the origin station\n          required: true\n          schema:\n            type: string\n            format: uuid\n            examples: [efdbb9d1-02c2-4bc3-afb7-6788d8782b1e]\n        - name: destination\n          in: query\n          description: The ID of the destination station\n          required: true\n          schema:\n            type: string\n            format: uuid\n            examples: [b2e783e1-c824-4d63-b37a-d8d698862f1d]\n\n\nHere the summary is fairly light on details, because that’s more of a title for the operation, but you can often elude to a fair bit within a few words, then expand on all that in the description.\n\n  /bookings:\n    get:\n      summary: List bookings for user\n      description: Returns a paginated list of trip bookings by the authenticated user.\n\n\nResponses\n\nWhether to go large in the response or not is not so clear cut. If you were explaining what was going to happen in the operation description, then the response is… that, which has already been explained. Duplicating that long form description in the response seems redundant.\n\nGenerally, a common practice is to keep descriptions short, and you can go two ways with this:\n\n  /bookings:\n    get:\n      summary: List existing bookings\n      description: Returns a list of all trip bookings by the authenticated user.\n      responses:\n        '200':\n          description: A list of bookings\n        '401':\n          description: Unknown user error\n        '403':\n          description: Forbidden from seeing list of bookings\n\n\nThis all seems a bit redundant, because the operation happy path is described at the top, and it’s really the errors that need more explaining. Depending on how you have errors set up, they should be explaining themselves, so this is really either the place for context which could not exist anywhere else, or a chance to just keep it simple.\n\n  /bookings:\n    get:\n      summary: List existing bookings\n      description: Returns a list of all trip bookings by the authenticated user.\n      responses:\n        '200':\n          description: OK\n        '401':\n          description: Unauthorized\n        '403':\n          description: Forbidden\n\n\nThis is essentially just repeating the HTTP status code because you need to write something, but you can do whatever you like. Essentially the advice would be: keep response descriptions short, unless there’s some really important context which has to be given to that specific endpoint, and then do whatever your API documentation tool is happy with. If it prefers shorter descriptions maybe put that context in the operation description."
        },
        {
          "id": "openapi-v3.2-documentation-external-documentation",
          "title": "External Documentation",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/documentation/external-documentation/",
          "content": "While the description property is excellent for giving a little more information about a specific tag, you might need to provide additional documentation if the business logic by a part of the API is complex, or there are lists of possible values defined outside of the API.\n\nIf anything requires further explanation, you can provide a link to an external web page where you offer a more detailed explanation using the externalDocs property.\n\nThe externalDocs property is two things, a URL using the url property, and a description explaining what this link is about.\n\ntags:\n  - name: diffs\n    description: Diff summary of changes in the API\n    externalDocs:\n      description: More details about Diff\n      url: https://docs.bump.sh/help/api-change-management/\n\n\n\nThis is not limited to tags, externalDocs can be used on:\n\n\n  Root Object\n  Tag Object\n  Operation Object\n  Schema Object\n\n\nHere’s all of them being used all at once!\n\nopenapi: 3.2.0\ninfo:\n  title: External Docs Everywhere!\n  version: \"1.0.0\"\n\nexternalDocs:\n  description: Guides &amp; Tutorials\n  url: https://docs.bump.sh/guides/\n\npaths:\n  /diffs:\n    get:\n      externalDocs:\n        description: Learn more about Operations\n        url: https://docs.bump.sh/openapi/v3.2/understanding-structure/paths-operations/\n\ntags:\n  - name: diffs\n    description: Diff summary of changes in the API\n    externalDocs:\n      description: More details about Diff\n      url: https://docs.bump.sh/help/api-change-management/\n\ncomponents:\n  schemas:\n    Diffs:\n      externalDocs:\n        url: https://docs.bump.sh/openapi/v3.2/data-models/schema-and-data-types/\n\n\n\nWhen you generate API documentation for the API description above, you’ll see the link rendered like this:"
        },
        {
          "id": "openapi-v3.2-understanding-structure-components",
          "title": "OpenAPI Components",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/understanding-structure/components/",
          "content": "Using $ref with Components\n  Create “Design Libraries” of Shared Components\n\n\nThe Components object in OpenAPI allows you to create reusable bits of OpenAPI that can then be pieced together like Lego blocks to build a better API description. This keeps things nice and tidy, and you can even spread them across multiple documents to share components between multiple APIs, or at least just keep your file sizes down.\n\ncomponents:\n  schemas:\n    User:\n      type: object\n      properties:\n        id:\n          type: integer\n        name:\n          type: string\n        email:\n          type: string\n          format: email\n  parameters:\n    userId:\n      name: id\n      in: path\n      description: ID of the user\n      required: true\n      schema:\n        type: integer\n  responses:\n    NotFound:\n      description: User not found\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Error'\n  requestBodies:\n    User:\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/User'\n  securitySchemes:\n    bearerAuth:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\n\n\nThe full list of objects which can be defined in components is:\n\n\n  callbacks - Define callback objects that send outgoing requests.\n  examples - Define reusable examples for whole media types.\n  headers - Define reusable HTTP header objects to be included in responses.\n  links - Define reusable links between operations.\n  parameters - Define reusable parameters that can be used in requests.\n  pathItems - Define reusable path items which can go into paths and webhooks.\n  requestBodies - Define reusable request body objects for operations.\n  responses - Define reusable response objects for operations.\n  schemas - Define reusable schemas for media types and any other object which accepts schemas.\n  securitySchemes - Define reusable security schemes for API authentication and authorization.\n\n\nUsing $ref with Components\n\nOnce components have been defined they can be referenced with $ref. This is mostly the same definition of $ref in JSON Schema so it can help to learn how that works, but there are a few caveats to keep in mind.\n\nThe OpenAPI Documentation from the OpenAPI Initiative includes a brilliant example of an API for playing the classic board game Tic Tac Toe, and it demonstrates $ref nicely.\n\nThis has several parts that are used several times, so instead of copy-pasting everything they’ve defined reusable components for both schemas and parameters.\n\npaths:\n  # Whole board operations\n  /board:\n    get:\n      summary: Get the whole board\n      description: Retrieves the current state of the board and the winner.\n      tags:\n        - Gameplay\n      operationId: get-board\n      responses:\n        \"200\":\n          description: \"OK\"\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/status\"\n  # Single square operations\n  /board/{row}/{column}:\n    parameters:\n      - $ref: \"#/components/parameters/rowParam\"\n      - $ref: \"#/components/parameters/columnParam\"\n    get:\n      # ... Hidden for readability...\n    put:\n      # ... Hidden for readability...\n\ncomponents:\n  parameters:\n    rowParam:\n      description: Board row (vertical coordinate)\n      name: row\n      in: path\n      required: true\n      schema:\n        $ref: \"#/components/schemas/coordinate\"\n    columnParam:\n      description: Board column (horizontal coordinate)\n      name: column\n      in: path\n      required: true\n      schema:\n        $ref: \"#/components/schemas/coordinate\"\n  schemas:\n    errorMessage:\n      type: string\n      maxLength: 256\n      description: A text message describing an error\n    coordinate:\n      type: integer\n      minimum: 1\n      maximum: 3\n      example: 1\n    mark:\n      type: string\n      enum: [\".\", \"X\", \"O\"]\n      description: Possible values for a board square. `.` means empty square.\n      example: \".\"\n    board:\n      type: array\n      maxItems: 3\n      minItems: 3\n      items:\n        type: array\n        maxItems: 3\n        minItems: 3\n        items:\n          $ref: \"#/components/schemas/mark\"\n    winner:\n      type: string\n      enum: [\".\", \"X\", \"O\"]\n      description: Winner of the game. `.` means nobody has won yet.\n      example: \".\"\n    status:\n      type: object\n      properties:\n        winner:\n          $ref: \"#/components/schemas/winner\"\n        board:\n          $ref: \"#/components/schemas/board\"\n\n\nHopefully this gives an idea of how $ref can be used, and if you’d like to learn more check out our advanced guide: Splitting Documents with $ref.\n\nCreate “Design Libraries” of Shared Components\n\nAn OpenAPI document does not need to contain paths or webhooks, it could be just a components object with nothing else.\n\nOne of more of these “components only” documents could then be shared around forming a rudimentary “design library”, helping teams reuse data models and various other bits across multiple APIs, multiple departments, or even externally to your organization.\n\nThere are various proprietary tools out there to help with this, but the concept can be achieved by just sharing these openapi-components.yaml or similar on your network drive, Git repository, intranet, or public website."
        },
        {
          "id": "openapi-v3.2-advanced-security",
          "title": "Security",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/advanced/security/",
          "content": "API Keys\n  HTTP Authentication    \n      Basic Authentication\n      Bearer Token Authentication\n      JWT Authentication\n    \n  \n  OAuth 2\n  OpenID Connect\n  Mutual TLS\n  Applying Security to Paths\n  Applying Security Globally\n  Overriding Path Security\n  Scopes\n  Referencing Security Schemes\n  Deprecating Security Schemes\n\n\nOpenAPI uses the term “security scheme” to cover both authentication and authorization schemes.\n\n\n  Authentication: Who is this user, are they who they say they are.\n  Authorization: What data can this user see, what actions can they take.\n\n\nOpenAPI v3.1 lets you describe APIs protected using the following security schemes:\n\n\n  HTTP authentication schemes (anything using the Authorization header)\n    \n      Basic\n      Bearer\n      Digest\n      OAuth (1.0)\n      others defined in RFC 9110 and HTTP Authentication Scheme Registry\n    \n  \n  API keys in headers, query string or cookies\n  OAuth 2.x\n  OpenID Connect Discovery\n  Mutual TLS\n\n\nSome of these are very niche and specific forms of security scheme, so we won’t get stuck into all of them. Let’s look at the most common ones for now.\n\ncomponents:\n  securitySchemes:\n    # API Key Authentication\n    ApiKeyHeader:\n      type: apiKey\n      in: header\n      name: X-API-Key\n    ApiKeyQuery:\n      type: apiKey\n      in: query\n      name: key\n    \n    # HTTP Authentication\n    HttpBasicAuth:\n      type: http\n      scheme: basic\n    HttpBearerToken:\n      type: http\n      scheme: bearer\n    JWT:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\n   \n    # OAuth 2.0 Authentication\n    OAuth2ReadWrite:\n      type: oauth2\n      flows:\n        authorizationCode:\n          scopes:\n            read: Grants read access\n            write: Grants write access\n          authorizationUrl: https://example.com/oauth/authorize\n          tokenUrl: https://example.com/oauth/token\n          refreshUrl: https://example.com/oauth/refresh\n\n\nThis is 90% of what you’ll bump into in the wild when it comes to security schemes.\n\nAPI Keys\n\nin: header\n\nAPI Keys in headers are often stored in some arbitrary header name like X-API-Key, which is frowned upon but is still commonly used.\n\n  ApiKeyHeader:\n    type: apiKey\n    in: header\n    name: X-API-Key\n\n\nThat would describe a request that looks like this:\n\nGET /bookings HTTP/2\nHost: api.example.com\nX-API-Key: &lt;your-api-key&gt;\n\n\n\n  Instead of specifically using X- headers, you can create custom headers with your company name at the start like Stipe-API-Key if something is specific to you. If it’s not then it’s better to use the standard HTTP headers so generic tooling knows how to work with them out of the box.\n\n\nin: query\n\nAPI Keys in query are also common, but best avoided for other reasons: it’s incredibly insecure.\n\nGET /bookings?key=&lt;your-api-key&gt; HTTP/2\nHost: api.example.com\n\n\nThe OWASP API Security Top 10 lists Broken Authentication as one of the top security issues for APIs, citing specifically “Sends sensitive authentication details, such as auth tokens and passwords in the URL.”\n\nGenerally the “API Key” scheme is best used for legacy API security schemes, which are being deprecated and replaced with better security schemes.\n\nHTTP Authentication\n\nHTTP Authentication is a widely used security scheme in OpenAPI. It allows you to authenticate and authorize users based on the Authorization header in the HTTP request.\n\nBasic Authentication\n\nOne of the most common HTTP authentication schemes is Basic Authentication. It involves sending the username and password in the Authorization header, encoded in Base64. Here’s an example:\n\nHttpBasicAuth:\n  type: http\n  scheme: basic\n\n\nTo make a request using Basic Authentication, the Authorization header would look like this:\n\nGET /bookings HTTP/2\nHost: api.example.com\nAuthorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=\n\n\nThat dXNlcm5hbWU6cGFzc3dvcmQ= is base64 encoded username:password, which is exactly why this security scheme is considered unwise for sending long-lived credentials. Short-lived frequently changing tokens might be less problematic especially if being sent over TLS.\n\nBearer Token Authentication\n\nBearer Token Authentication is another popular HTTP authentication scheme. It involves sending a token in the Authorization header, prefixed with the word Bearer. Here’s an example:\n\nHttpBearerToken:\n  type: http\n  scheme: bearer\n\n\nTo make a request using Bearer Token Authentication, the Authorization header would look like this:\n\nGET /bookings HTTP/2\nHost: api.example.com\nAuthorization: Bearer &lt;your-token-here&gt;\n\n\nThis token is in plain text, but it’s usually randomly generated and short lives so it’s not so concerning if somebody nabs it, especially if you’re using JWT or OAuth 2 which operates over bearer.\n\nJWT Authentication\n\nUsing a JWT (JSON Web Token) allows for more advanced authentication scenarios and includes additional information in the token payload. Here’s an example:\n\nJWT:\n  type: http\n  scheme: bearer\n  bearerFormat: JWT\n\n\nIn OpenAPI a JWT security scheme is actually just Bearer Token Authentication again, with bearerFormat: JWT letting documentation tools know to mention that the token is a JWT. It doesn’t make much difference, its just how the token was generated before it was passed to the API.\n\nTo make a request using JWT Authentication, the Authorization header would look similar to Bearer Token Authentication:\n\nGET /bookings HTTP/2\nHost: api.example.com\nAuthorization: Bearer &lt;your-jwt-here&gt;\n\n\nOAuth 2\n\nOAuth 2 is a widely used authorization framework that allows users to grant third-party applications access to their resources without sharing their credentials. In OpenAPI, you can describe OAuth 2 authentication using the following example:\n\nOAuth2ReadWrite:\n  type: oauth2\n  flows:\n    authorizationCode:\n      scopes:\n        read: Grants read access\n        write: Grants write access\n      authorizationUrl: https://example.com/oauth/authorize\n      tokenUrl: https://example.com/oauth/token\n      refreshUrl: https://example.com/oauth/refresh\n\n\nIn this example, the OAuth2ReadWrite security scheme represents OAuth 2 authentication. It uses the authorizationCode flow, which is one of the most common flows in OAuth 2, but there is also clientCredentials, implicit, password, and deviceAuthorization flows.\n\nThe scopes section defines the available scopes for this security scheme, such as read and write. The authorizationUrl specifies the URL where users can authorize the application, the tokenUrl is used to obtain access tokens, and the refreshUrl is used to refresh expired tokens.\n\nTo apply OAuth 2 authentication to a specific operation, you can use the security keyword and specify the security scheme and scopes:\n\npaths:\n  /bookings:\n    get:\n      security:\n        - OAuth2ReadWrite: [read]\n\n\nIn this example, the /bookings path requires the OAuth2ReadWrite security scheme with the read scope for the GET operation.\n\nOpenID Connect\n\nOpenID Connect is an identity layer built on top of OAuth 2.0 that allows clients to verify the identity of end-users based on the authentication performed by an authorization server. It provides a standardized way to authenticate users and obtain their identity information, such as name, email, and profile picture.\n\nIn OpenAPI v3.1, you can describe OpenID Connect authentication using the following example:\n\nOpenIDConnect:\n  type: openIdConnect\n  openIdConnectUrl: https://example.com/.well-known/openid-configuration\n\n\nIn this example, the OpenIDConnect security scheme represents OpenID Connect authentication. The openIdConnectUrl specifies the URL where the OpenID Connect provider’s configuration can be found. This configuration includes important information such as the authorization endpoint, token endpoint, and public keys used for verifying ID tokens.\n\nTo apply OpenID Connect authentication to a specific operation, you can use the security keyword and specify the security scheme:\n\npaths:\n  /bookings:\n    get:\n      security:\n        - OpenIDConnect: []\n\n\nIn this example, the /bookings path requires the OpenIDConnect security scheme for the GET operation.\n\nOpenID Connect is a powerful authentication mechanism that allows you to leverage existing identity providers and provide a seamless login experience for your users. It is widely adopted and supported by major identity providers such as Google, Microsoft, and Okta.\n\nMutual TLS\n\nMutual TLS (Transport Layer Security) is an authentication type in OpenAPI that provides a secure way to authenticate both the client and the server. It involves the exchange of digital certificates between the client and the server to establish a trusted connection, meaning it’s commonly used in scenarios where strong authentication and secure communication are required: banking, healthcare, and other sensitive industries.\n\nWith mutual TLS authentication, the client presents its own certificate to the server during the TLS handshake process. The server then verifies the client’s certificate against its trusted certificate authority (CA) to ensure the client’s identity. Similarly, the server presents its own certificate to the client, and the client verifies the server’s certificate.\n\nThere’s not much to it, you just need to define the security scheme in your OpenAPI document:\n\nMutualTLS:\n  type: mutualTLS\n\n\nThis is basically just a flag to let clients know they’ll need to set up Mutual TLS or no connections are going to work, but the specifics of that happen outside of OpenAPI. It takes a bit of work, but this authentication method adds an extra layer of security by ensuring that both the client and the server are authenticated before any data is exchanged. It helps prevent unauthorized access and protects against man-in-the-middle attacks.\n\nApplying Security to Paths\n\nThe security schemes are just setting there until they are applied to paths in your OpenAPI document. You can use the security keyword at the path level to do this:\n\nopenapi: 3.2.0\npaths:\n  /bookings:\n    get:\n      security:\n        - HttpBearerToken: []\n\n\nIn this example, the /bookings path requires the HttpBearerToken security scheme for all requests.\n\nopenapi: 3.2.0\npaths:\n  /bookings:\n    get:\n      security:\n        - ApiKeyQuery: []\n        - HttpBearerToken: []\n\n\nIn this example either the ApiKeyHeader or HttpBearerToken security schemes, meaning a client could use either. This is great when one method is being deprecated.\n\nSecurity schemes can also be applied as an AND:\n\nopenapi: 3.2.0\npaths:\n  /bookings:\n    get:\n      security:\n        - ApiKeyHeader: []\n          HttpBearerToken: []\n\n\nThis example would only pass if both the ApiKeyHeader and HttpBearerToken are passed.\n\nApplying Security Globally\n\nTo avoid needing to set similar security keywords on every path (and possibly missing a few) you can apply security globally.\n\nopenapi: 3.2.0\nsecurity:\n  - HttpBearerToken: []\npaths:\n  /bookings:\n    get:\n      # ...\n\n\nThis will now be applied to all paths unless turned off.\n\nOverriding Path Security\n\nGlobally applied rules can be overridden with a path-level security keyword.\n\nopenapi: 3.2.0\nsecurity:\n  - HttpBearerToken: []\npaths:\n  /bookings:\n    get:\n      security: [] # no security\n\n\nAll other paths will continue to be secured regardless of what order they’re in.\n\nScopes\n\nYou might have noticed the empty array showing up: HttpBearerToken: []. This empty array is where “scopes” go.\n\nScopes allow you to define fine-grained permissions within some types of security schema that support them, and in OpenAPI v3.1 that means OAuth 2 and OpenID Connect.\n\nEach security scheme can have its own set of scopes, which can be used to control access to specific resources or actions.\n\nOAuth2ReadWrite:\n  type: oauth2\n  flows:\n    authorizationCode:\n      scopes:\n        read: Grants read access\n        write: Grants write access\n\n\nIn this example, the OAuth2ReadWrite security scheme has two scopes: read and write, which can be used to check certain actions, maybe limiting GET to read and POST, PUT, PATCH, and DELETE to write.\n\nTo apply scopes to specific operations, you can use the security keyword and fill in that array at the end with the relevant scopes.\n\npaths:\n  /bookings:\n    get:\n      security:\n        - OAuth2ReadWrite: [read]\n    post:\n      security:\n        - OAuth2ReadWrite: [write]\n\n\nYou can get a lot more advanced with scopes than this, with some API developers breaking down the API into manageable chunks. Here are just a few from the Shopify API as an example:\n\nread_orders\nwrite_orders\n\nread_content\nwrite_content\n\nread_customers\nwrite_customers\n\nread_customer_payment_methods\nwrite_customer_payment_methods\n\n\nReferencing Security Schemes\n\nYou can reference security schemes in OpenAPI v3.2 using the $ref keyword, just like you would with other components. This allows you to reuse security schemes across different paths or operations.\n\npaths:\n  /bookings:\n    get:\n      security:\n        - $ref: '#/components/securitySchemes/HttpBearerToken'\ncomponents:\n  securitySchemes:\n    HttpBearerToken:\n      type: http\n      scheme: bearer\n\n\nThis example references the HttpBearerToken security scheme defined in the components section. This is useful for keeping an OpenAPI document DRY (Don’t Repeat Yourself), especially when the components are spread across multiple documents.\n\nDeprecating Security Schemes\n\nAs APIs evolve over time and security practices change, the authorization methods and security schemes change with them.\n\nCreating a new major version for an API purely to ditch security schemes is heavy handed and unnecessary. Instead you can add a new security scheme as an option, then deprecate the unwanted scheme to steer new users away from it.\n\nOpenAPI v3.2 introduced the deprecated keyword for security schemes to help make this easier to document, and it works the same as other deprecable objects like operations, headers, and parameters.\n\ncomponents:\n  securitySchemes:\n\n    ApiKeyHeader:\n      type: apiKey\n      in: header\n      name: X-API-Key\n      deprecated: true\n\n    HttpBearerToken:\n      type: http\n      scheme: bearer\n\n\nThis example would help steer users away from the custom X-API-Key, and would help returning users notice they should probably update. Other deprecation tricks exist, but this is a handy way to clearly mark it as deprecated on API documentation and other tooling."
        },
        {
          "id": "openapi-v3.2-data-models-schema-and-data-types",
          "title": "Schemas and Data Types",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/data-models/schema-and-data-types/",
          "content": "Data Formats\n  Validation    \n      const &amp; enum\n      default\n      minimum &amp; maximum\n      enum\n      pattern\n      required\n    \n  \n  readOnly &amp; writeOnly\n  Learn more about JSON Schema\n\n\nOne of the most important parts of OpenAPI is the schema object. Schema objects are used to describe HTTP request and response bodies, parameters, headers, and all sorts of other data, whether its JSON, XML, or primitive types like integers and strings.\n\n\n  If you’re familiar with JSON Schema, you’ll be right at home here, because OpenAPI v3.1 uses JSON Schema (draft 2020-12). For those who have not used JSON Schema before, that’s ok, follow along.\n\n\nThe first thing to learn about a schema is the type keyword, which can be one or more of the following types:\n\n\n  null\n  boolean\n  object\n  array\n  number\n  string\n\n\n\n  You can also use “integer” for the sake of convenience, but “integer” and “number” are basically identical because JSON itself does not make that distinction. Since there is no distinct JSON integer type, JSON Schema defines integers mathematically. This means that both 1 and 1.0 are equivalent, and are both considered to be integers.\n\n\nA schema object looks like this:\n\ntype: string\n\n\nIt can also define multiple types, for instance making a property nullable:\n\ntype: [string, null]\n\n\nIt also allows you to support properties that could be either a number or a numeric string.\n\ntype: [number, string]\n\n\nDescribing an object will often involve the use of properties to define what each of the objects properties should look like, and each of those properties will have a “subschema” that describes it.\n\ntype: object\nrequired:\n- name\nproperties:\n  name:\n    type: string\n  age:\n    type: integer\n\n\nArrays work in a similar way, with an items keyword allowing each item in the array to be described.\n\ntype: array\nitems:\n  type: string\n\n\nArrays of objects can be described by combining the two concepts:\n\ntype: array\nitems:\n  type: object\n  required:\n  - name\n  properties:\n    name:\n      type: string\n    age:\n      type: integer\n\n\nData Formats\n\nThe type keyword sets out the basic data type, but knowing something is a string or an integer is just the first step to understanding what that data is all about.\n\nFirst of all there’s the format keyword, which covers a predefined set of common formats:\n\ntype: array\nitems:\n  type: object\n  required:\n  - name\n  properties:\n    name:\n      type: string\n    email:\n      type: string\n      format: email\n    age:\n      type: integer\n      format: int32\n\n\nThe full list of formats defined in the JSON Schema Validation that OpenAPI v3.1 relies upon:\n\n\n  \n    date-time: A string instance is valid against this attribute if it is a valid representation according to the “date-time” production as defined in RFC3339.\n  \n  \n    date: A string instance is valid against this attribute if it is a valid representation according to the “full-date” production as defined in RFC3339.\n  \n  \n    time: A string instance is valid against this attribute if it is a valid representation according to the “full-time” production as defined in RFC3339.\n  \n  \n    duration: A string instance is valid against this attribute if it is a valid representation according to the “duration” production as defined in RFC3339.\n  \n  \n    email: As defined by the “Mailbox” ABNF rule in RFC 5321, section 4.1.2 RFC5321.\n  \n  \n    idn-email: As defined by the extended “Mailbox” ABNF rule in RFC 6531, section 3.3 RFC6531.\n  \n  \n    hostname: As defined by RFC 1123, section 2.1 RFC1123, including host names produced using the Punycode algorithm specified in RFC 5891, section 4.4 RFC5891.\n  \n  \n    idn-hostname: As defined by either RFC 1123 as for hostname, or an internationalized hostname as defined by RFC 5890, section 2.3.2.3 RFC5890.\n  \n  \n    ipv4: An IPv4 address according to the “dotted-quad” ABNF syntax as defined in RFC 2673, section 3.2 RFC2673.\n  \n  \n    ipv6: An IPv6 address as defined in RFC 4291, section 2.2 RFC4291.\n  \n  \n    uri: A string instance is valid against this attribute if it is a valid URI, according to RFC3986.\n  \n  \n    uri-reference: A string instance is valid against this attribute if it is a valid URI Reference (either a URI or a relative- reference), according to RFC3986.\n  \n  \n    iri: A string instance is valid against this attribute if it is a valid IRI, according to RFC3987.\n  \n  \n    iri-reference: A string instance is valid against this attribute if it is a valid IRI Reference (either an IRI or a relative- reference), according to RFC3987.\n  \n  \n    uuid: A string instance is valid against this attribute if it is a valid string representation of a UUID, according to RFC4122.\n  \n  \n    uri-template: A string instance is valid against this attribute if it is a valid URI Template (of any level), according to RFC6570.\n  \n  \n    json-pointer: A string instance is valid against this attribute if it is a valid JSON string representation of a JSON Pointer, according to RFC 6901, section 5 RFC6901.\n  \n  \n    relative-json-pointer: A string instance is valid against this attribute if it is a valid Relative JSON Pointer relative-json-pointer.\n  \n  \n    regex - A regular expression, which SHOULD be valid according to the ECMA-262 ecma262 regular expression dialect\n  \n\n\nYou can also define your own custom formats, which tooling will not understand, but that doesn’t matter as the specification tells tooling to ignore unknown formats.\n\nValidation\n\nIn addition to defining data types and formats, JSON Schema provides several validation keywords to enforce specific constraints on the data. Here are a few popular validation keywords:\n\nconst &amp; enum\n\nRestricting a value down to one or more potential values can be done with the const or enum keywords.\n\nFirst, a look at enum, as that keyword has been around longer and is more used:\n\ntype: string\nenum:\n  - pending\n  - fulfilled\n  - archived\n\n\nThis says the string can’t just be any old string, it has to be one of the approved values listed in enum.\n\n\n  Learn more about const on JSON-Schema.org: Enumerated Values.\n\n\nOpenAPI v3.1 gained the const keyword added in modern JSON Schema, which helps with describing something that can only ever be one value.\n\nThe JSON Schema tutorial uses the example of having a country field where you only support shipping to the United States for export reasons:\n\nproperties:\n  country:\n    const: United States of America\n\n\nThat’s one way to use it, but another is to act as a switch in a oneOf.\n\noneOf:\n  - title: Card\n    properties:\n      object:\n        type: string\n        const: card\n      number:\n        type: string\n      cvc:\n        type: integer\n      exp_month:\n        type: integer\n      exp_year:\n        type: integer\n  \n  - title: Bank Account\n    type: object\n    properties:\n      object:\n        const: bank_account\n        type: string\n      number:\n        type: string\n      sort_code:\n        type: string\n\n\nIn this example the object could be card or bank_account, but instead of defining that as an enum and the other properties all have to figure out whether they relate to cards or bank accounts, we use the const to help match the subschema.\n\n\n  Learn more about const on JSON-Schema.org: Constant Values, and read our guide on Schema Composition to learn more about oneOf.\n\n\ndefault\n\nSetting a default lets people and code know what to do when a value has not been provided.\n\ntype: string\ndefault: pending\nenum:\n  - pending\n  - fulfilled\n  - archived\n\n\nThis is useful for properties that have a common or expected value, allowing you to avoid having to specify it every time.\n\nminimum &amp; maximum\n\nThe minimum and maximum keywords allow you to specify the minimum and maximum values for numeric properties. For example:\n\ntype: number\nminimum: 0\nmaximum: 100\n\n\nThis schema ensures that the value of the property falls within the range of 0 to 100.\n\nenum\n\nThe enum keyword allows you to define a list of acceptable values for a property. For example:\n\ntype: string\nenum:\n  - apple\n  - banana\n  - orange\n\n\nThis schema restricts the property value to be one of the specified options: “apple”, “banana”, or “orange”.\n\npattern\n\nThe pattern keyword allows you to enforce a specific regular expression pattern for string properties. For example:\n\ntype: string\npattern: ^[A-Za-z]+$\n\n\nThis schema ensures that the property value consists of only alphabetic characters.\n\nrequired\n\nThe required keyword is used to specify the required properties within an object. For example:\n\ntype: object\nrequired:\n  - name\n  - age\n\n\nThis schema mandates that the properties “name” and “age” must be present in the object.\n\nFor more information on JSON Schema validation keywords, you can refer to the JSON Schema Validation documentation.\n\nreadOnly &amp; writeOnly\n\nJSON Schema provides readOnly and writeOnly boolean keywords, which are really helpful in the context of an API, because resources are usually available in two flavours: the representation of a resource in a request body, and the representation of the resource in a response body.\n\n\n  \n    readOnly: true indicates that a value should not or cannot be be modified, but can be seen (e.g., id, created_at).\n  \n  \n    writeOnly: true indicates that a value may be set, but will remain hidden (e.g., password, or PII like the cvc security code on a credit card).\n  \n\n\ntype: object\nproperties:\n  id:\n    type: string\n    readOnly: true\n  username:\n    type: string\n  date_of_birth:\n    type: string\n    format: date-time\n  password:\n    type: string\n    writeOnly: true\n  created_at:\n    type: string\n    format: date-time\n    readOnly: true\n\n\nBy using these, a single schema can serve both requests and responses. For example, a User schema can send password during creation (POST), but exclude it in the GET response, while fields like id and created_at are only returned. This approach reduces duplication, making schemas easier to maintain.\n\nThese two keywords are considered “annotations” in JSON Schema, which means they are only there for various bits of tooling to do something with if they like. There is no requirement for tools to do anything in particular, but a common convention for most documentation tools will be to skip listing a readOnly property for a HTTP POST/PATCH request body, and similarly skip documenting writeOnly properties in a response body.\n\nThis same approach extends to example request/responses of JSON data generated by documentation tools, and also mock servers, and even change the parameters available for generated SDK code.\n\n\n  There are some complexities here with HTTP PUT because you’re meant to be sending the entire resource each time whether something is writeable or not, but if a property was removed from a sample request for an HTTP PUT request, that means: a) “send it or not, we don’t care”, or b) not sending it will result in it being removed. Seeing as tooling varies on this, and API implementations vary in how they interpret missing values in PUT, you just need to check the tools you use do what your API expects.\n\n\nLearn more about JSON Schema\n\nThere is a lot more to JSON Schema and OpenAPI Schema Objects than we’ve covered here, but this will hopefully get you off to a good start. If you need to learn more, you can read our guide on JSON Schema in OpenAPI."
        },
        {
          "id": "openapi-v3.2-documentation-grouping-operations-with-tags",
          "title": "Organize API Endpoints with OpenAPI Tags",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/documentation/grouping-operations-with-tags/",
          "content": "Describing Tags for Better Documentation\n  Tags Linking to Additional Documentation\n  Tags Ordering Groups in Documentation\n  Different Kinds of Tags\n  Nested Tag Structures\n  OpenAPI Tags Best Practices    \n      Tag Everything\n      Make Every Tag Unique\n      Define All Your OpenAPI Tags Before Use\n    \n  \n  Conclusion\n\n\nTags are a great way to organize the API endpoints in your OpenAPI documents.\n\nTypically, OpenAPI tags are used to group related endpoints in a meaningful way, such as by business function or logical objects. When using tags, you define an array of tags at the root of your document, like this:\n\ntags:\n  - name: stations\n    summary: Stations\n\n  - name: trips\n    summary: Trips\n\n  - name: bookings\n    summary: Bookings\n\n  - name: payments\n    summary: Payments\n\n\nOnce you’ve created these tags, you can use them to group related endpoints in your API using the tags property on the endpoint as follows:\n\npaths:\n  /stations:\n    get:\n      summary: Get a list of train stations\n      tags: [stations]\n  /trips:\n    get:\n      summary: Get available train trips\n      tags: [trips]\n\n\nYou can also apply multiple tags to an operation:\n\npaths:\n  /bookings/{bookingId}/payment:\n    post:\n      summary: Pay for a Booking\n      tags: [bookings, payments]\n\n\nDescribing Tags for Better Documentation\n\nWhen specifying your tags in the root level of an OpenAPI document, you can give context to the tag using the description property.\n\nLet’s take the Train Travel API as an example. Here is how the tags are created as described:\n\ntags:\n  - name: stations\n    summary: Stations\n    description: | \n      Find and filter train stations across Europe, including their location\n      and local timezone.\n\n  - name: trips\n    summary: Trips\n    description: | \n      Timetables and routes for train trips between stations, including pricing\n      and availability.\n  \n  - name: bookings\n    summary: Bookings\n    description: | \n      Create and manage bookings for train trips, including passenger details\n      and optional extras.\n  \n  - name: payments\n    summary: Payments\n    description: |\n      Pay for bookings using a card or bank account, and view payment\n      status and history.\n\n      &gt; warn\n      &gt; Bookings usually expire within 1 hour so you'll need to make your payment\n      &gt; before the expiry date \n\n\n\nSee it live\n\nNote that you can use Markdown in the description field to better describe your tags.\n\nTags Linking to Additional Documentation\n\nWhile the description property is excellent for giving a little more information about a specific tag, you might need to provide additional documentation if the business logic or object represented by the tag is complex and requires further explanation. Let’s take our Diffs example from above. You can provide a link to an external web page where you offer a more detailed explanation using the externalDocs property.\n\nIn the code snippet below, the externalDocs property provides a link to a URL using the url property. A description for the URL can also be specified using the description property.\n\ntags:\n  - name: stations\n    description: Find and filter train stations across Europe, including their location and local timezone.\n    externalDocs:\n      description: Working with Stations\n      url: /docs/guides/working-with-stations\n\n\nWhen you generate API documentation for the API contract above, you’ll see the link rendered like this:\n\n\n\nTags Ordering Groups in Documentation\n\nWhen specifying OpenAPI tags in the root of your API contract, the order in which you list the tags will define the order in which they appear in the generated documentation. This ordering lets you sort the tags meaningfully.\n\ntags:\n  - name: stations\n    summary: Stations\n    description: Find and filter train stations across Europe, including their location and local timezone.\n  - name: trips\n    summary: Trips\n    description: Timetables and routes for train trips between stations, including pricing and availability.\n  - name: bookings\n    summary: Bookings\n    description: Create and manage bookings for train trips, including passenger details and optional extras.\n  - name: payments\n    summary: Payments\n    description: Pay for bookings using a card or bank account, and view payment status and history.\n\n      &gt; warn\n      &gt; Bookings usually expire within 1 hour so you'll need to make your payment\n      &gt; before the expiry date\n\n\nWhen you generate API documentation, you’ll notice the documentation orders the endpoint groups in the same way:\n\n\nSee it live\n\nNote that Bump.sh helps you order your endpoints and webhooks using a “Group by tag” operation. It is actually the default behaviour of Bump.sh when you have these tags defined and have not selected an other sorting option for your Bump.sh API documentation.\n\nNow that you understand what tags are and their benefits, you’ll see some best practices you should follow when using OpenAPI tags in API contracts.\n\nDifferent Kinds of Tags\n\nUsing the kind keyword for tags added in OpenAPI v3.2, tags can be categorized for different purposes.\n\nAny string value can be used; common uses are nav for Navigation, badge for visible badges, audience for APIs used by different groups.\n\ntags: \n  # Navigation Tags\n  - name: stations\n    summary: Stations\n    kind: nav\n\n  - name: trips\n    summary: Trips\n    kind: nav\n  \n  - name: bookings\n    summary: Bookings\n    kind: nav\n  \n  - name: payments\n    summary: Payments\n    kind: nav\n\n  # Audience Tags\n  - name: beta\n    kind: audience\n\n  - name: internal\n    kind: audience\n\n  - name: public\n    kind: audience\n\n\nA registry of the most commonly used values is available here, and more conventions will appear over time.\n\nNested Tag Structures\n\nBy default using tags creates a flat grouping of operations, which is usually enough for smaller APIs, but larger APIs with hundreds of operations might need more structure especially for navigation purposes.\n\nOpenAPI v3.2 introduced the parent keyword, which allows for tags to be given a parent-child relationship.\n\ntags: \n  - name: stations\n    summary: Stations\n    kind: nav\n  \n  - name: bookings\n    summary: Bookings\n    kind: nav\n  \n  - name: payments\n    summary: Payments\n    kind: nav\n    parent: bookings\n\n\nHow this gets implemented by different tools is up to the tooling maintainers to decide, but the most common implementation for API documentation tools, especially for kind: nav, will be to treat “no parent” tags as top-level navigation, and nested items as expandable groups.\n\nOpenAPI Tags Best Practices\n\nTag Everything\n\nTo ensure your endpoints remain logically grouped and ordered, always tag every endpoint, even if it means creating a tag for a single endpoint.\n\nUntagged endpoints will not show up under any big section represented by a tag of your documentation generated by Bump.sh or most other tools, so things can “go missing” if you only tag some endpoints.\n\nMake Every Tag Unique\n\nWhen defining the list of tags in the root of your API contract, make sure not to duplicate tag names. Since the tag’s name property links an endpoint to a tag, duplicate names are likely to confuse developers looking at the API contract.\n\nThe code snippet below contains the root Tag Object in an API contract. Notice how the Validations tag has been duplicated, and the second definition contains a different description to the first:\n\ntags:\n  - name: version\n    summary: Versions\n    description: Deploy your API contracts\n  - name: validation\n    summary: Validations\n    description: Check &amp; validate your API contracts\n  - name: hub\n    summary: Hubs\n    description: Interact with your Hubs\n  - name: validation\n    summary: Validations\n    description: Validate your API status\n\n\nThese duplicate tags would confuse anyone trying to understand your API contract, as they wouldn’t know which of the two tag definitions an endpoint belongs to.\n\nInstead, make sure you define and describe every tag only once in the root Tag Object, like in the snippet below:\n\ntags:\n  - name: versions\n    summary: Versions\n    description: Deploy your API contracts\n  - name: validations\n    summary: Validations\n    description: Check &amp; validate your API contracts\n  - name: hubs\n    summary: Hubs\n    description: Interact with your Hubs\n\n\nDefine All Your OpenAPI Tags Before Use\n\nThe OpenAPI specification doesn’t require you to define all your tags in the root Tag Object of your API contract. This means you can add a tag to an endpoint without listing it in the root Tag Object, but this is a bad idea. You won’t be able to control what order the OpenAPI tags should appear in, and you won’t be able to add a description or provide a link to external documentation for that tag. It can also confuse developers browsing the API documentation as they won’t see a list of all the tags used in the API description.\n\nAs an example, consider the code snippet below where the Previews and the Ping tags has not been included in the root Tag Object:\n\ntags:\n  - name: diffs\n    summary: Diffs\n    description: Diff summary of changes in the API\n  # Missing Previews tag\n  # Missing Ping tag\n  - name: versions\n    summary: Versions\n    description: Deploy your API contracts\n  - name: validations\n    summary: Validations\n    description: Check &amp; validate your API contracts\n  - name: hubs\n    summary: Hubs\n    description: Interact with your Hubs\n\npaths:\n  /diffs:\n    post:\n      tags: [ diffs ]\n  /diffs/{id}:\n    get:\n      tags: [ diffs ]\n  /hubs/{hub_id_or_slug}:\n    get:\n      tags: [ hubs ]\n  /versions:\n    post:\n      tags: [ versions ]\n  /validations:\n    post:\n      tags: [ validations ]\n  /previews:\n    post:\n      tags: [ previews ]\n  /previews/{preview_id}:\n    put:\n      tags: [ previews ]\n  /versions/{version_id}:\n    get:\n      tags: [ versions ]\n  /ping:\n    get:\n      tags: [ ping ]\n\n\nWhen you generate the documentation, notice how the Previews and Ping sections are at the bottom of the list.\n\n\n\nThis incorrect ordering and lack of description will make this section much harder to understand for a developer consuming your API.\n\nOn the other hand, notice how every endpoint in the OpenAPI below has a tag also defined in the root Tag Object:\n\ntags:\n  - name: diffs\n    summary: Diffs\n    description: Diff summary of changes in the API\n  - name: ping\n    summary: Ping\n    description: Check the API status\n  - name: previews\n    summary: Previews\n    description: Preview changes to an API Documentation\n  - name: versions\n    summary: Versions\n    description: Deploy your API contracts\n  - name: validations\n    summary: Validations\n    description: Check &amp; validate your API contracts\n  - name: hubs\n    summary: Hubs\n    description: Interact with your Hubs\n\npaths:\n  /diffs:\n    post:\n      tags: [ diffs ]\n  /diffs/{id}:\n    get:\n      tags: [ diffs ]\n  /hubs/{hub_id_or_slug}:\n    get:\n      tags: [ hubs ]\n  /versions:\n    post:\n      tags: [ versions ]\n  /validations:\n    post:\n      tags: [ validations ]\n  /previews:\n    post:\n      tags: [ previews ]\n  /previews/{preview_id}:\n    put:\n      tags: [ previews ]\n  /versions/{version_id}:\n    get:\n      tags: [ versions ]\n  /ping:\n    get:\n      tags: [ ping ]\n\n\nBy doing this, your documentation will display the endpoint groups in the correct order along with the tag’s description.\n\n\n\nConclusion\n\nIn this article, you learned more about OpenAPI tags and their value in an API description. You also learned that you can add human-readable labels, descriptions, and external documentation links to the tag, along with more advanced usage like categorization and nesting. This article has also shown you some best practices to follow when using tags that can improve the quality of your generated documentation."
        },
        {
          "id": "openapi-v3.2-understanding-structure-api-servers",
          "title": "Defining API Servers",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/understanding-structure/api-servers/",
          "content": "Names and Descriptions\n  Server Variables\n  Best Practices    \n      Versioning APIs\n      Using Descriptive Names\n      Avoiding Sensitive Information\n    \n  \n\n\nThe servers section in an OpenAPI specification serves as a roadmap, detailing the various environments your API is accessible from. It’s a straightforward yet powerful way to communicate the base URLs of your API across different stages of its lifecycle, or in different environments the end-users might be interested in like a mocking server, or a sandbox for interacting with the API without real-world consequences.\n\nHere is an example of how you can define API servers in your OpenAPI specification:\n\nopenapi: 3.2.0\ninfo:\n  title: Example API\n  version: 1.0.0\n\nservers:\n  - name: Development\n    url: http://localhost:8088/api\n    description: Local development server\n    x-internal: true\n\n  - name: Staging\n    url: https://staging.example.com/api\n    description: Staging server used for testing and QA\n    x-internal: true\n\n  - name: Production\n    url: https://example.com/api\n    description: Production server for live traffic\n    x-internal: false\n\n\nThis example shows three API servers, for the common dev, staging, and production environments. Perhaps the local environment is on localhost and perhaps its a virtual machine on the cloud somewhere, but the idea is that same, you have all the different places an API might be.\n\nNames and Descriptions\n\nIn OpenAPI v3.2, name and description fields are optional. The name field is a short label for the server, while the description field provides more context about its intended use.\n\nPrior to OpenAPI v3.2 the name property did not exist, and the human-readable name went into description. This was a bit confusing, as everywhere else in OpenAPI uses description for longer form instructions. Some tooling and OpenAPI usage might be a little behind, so switch from description only to name and description when tooling supports OpenAPI v3.2 properly.\n\n\n  The x-internal is not strictly part of the specification, but it is a popular extension. Any tools that support it will hide these servers, removing them from user facing documentation for example. This lets you can keep handy development and testing information in OpenAPI, but avoid confusing end-users with details about your internal setup.\n\n\nServer Variables\n\nServer variables offer a convenient way to modify server URLs, covering simple patterns such as environment names, geographical regions, or covering wildcards like user-generated subdomains. These variables are part of the server object, and allow for more flexible API configurations without hardcoding every possible server option.\n\nFor instance, consider an API that is deployed across multiple regions, such as the United States, Europe, and Asia. Instead of listing each server URL separately, you can use a server variable to represent the region.\n\nservers:\n  - name: Production Server\n    url: \"https://{region}.api.example.com\"\n    description: Live server for production traffic, directing yourself to the appropriate region.\n    variables:\n      region:\n        default: eu\n        description: Geographic regions which have a dedicated server. Pick the closest one.\n        enum:\n          - us\n          - eu\n          - asia\n\n\nIn this example, {region} is a server variable, and the enum restricts this to three possible values: us, eu, and asia. The default value is eu, which means if the region is not specified, tooling can know which value to use. This setup allows clients to dynamically select the appropriate regional server by substituting the {region} variable in the URL template, resulting in https://asia.api.example.com.\n\nBest Practices\n\nVersioning APIs\n\nSome people try to use server variables for handling API Versions (v1, v2, v3) in a single OpenAPI document. This is a poor fit for server variables, because far more than the server URL will change between major versions. Server variables help when just the server is changing, but the other operations and components are the same.\n\nIt’s better to consider two different versions of an API as two different APIs, and use the servers section to document them separately. This way, you can have a clear separation of concerns and avoid confusion for users who may be working with multiple versions of your API.\n\nUsing Descriptive Names\n\nWhen defining servers, use descriptive names that clearly indicate the purpose of each server. This helps users quickly identify the server they need to interact with.\n\nAvoiding Sensitive Information\n\nAvoid including sensitive information in server URLs, such as API keys or credentials. Server variables can help with this by allowing you to define placeholders for sensitive data, which can be populated at runtime."
        },
        {
          "id": "openapi-v3.2-understanding-structure-basic-structure",
          "title": "Basic Structure",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/understanding-structure/basic-structure/",
          "content": "The OpenAPI Document    \n      1. OpenAPI Object\n      2. Info Object\n      3. Servers Object\n      4. Paths Object\n      5. Components Object\n      6. Security Object\n      7. Webhooks Object\n      8. Tags Object\n    \n  \n  Example OpenAPI Document\n\n\nThe OpenAPI Specification (OAS) is an “API Description Format”, providing a standard format for describing REST APIs, making it easier to design, document, and consume them.\n\nIn this tutorial we’ll explore the structure of an OpenAPI document, focusing on the main sections and important elements, so that you can get a feel for where everything is, without having to scan through the whole OpenAPI Specification yourself.\n\nThe OpenAPI Document\n\nAn OpenAPI document is typically formatted as either YAML or JSON (RFC8259). The document itself is usually named openapi.yaml or openapi.json, but it could have any name.\n\nIt consists of several key sections that describe the API’s endpoints, requests, responses, data models, and more. Here is an outline of the main sections:\n\n\n  OpenAPI Object\n  Info Object\n  Servers Object\n  Paths Object\n  Components Object\n  Security Object\n  Webhooks Object\n  Tags Object\n\n\nLet’s look at each of these objects to see what’s going on.\n\n1. OpenAPI Object\n\nThe root of the OpenAPI document is the openapi object, which specifies the version of the OpenAPI Specification being used. For example:\n\nopenapi: 3.2.0\n\n\nThis is required, so that tooling knows which version you are working with. The 3.2 part is the important bit. The patch number (3.2.0) doesn’t really matter as those “patch” versions only add clarifications to the specification and never change meaning, but it’s helpful to know what version somebody was reading when they wrote the OpenAPI.\n\n2. Info Object\n\nThe info object holds general metadata about the API, such as the title, version, description, and contact information.\n\ninfo:\n  title: Train Travel API\n  description: |\n    API for finding and booking train trips across Europe.\n  version: 1.0.0\n  contact:\n    name: Train Support\n    url: https://example.com/support\n    email: support@example.com\n  license:\n    name: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International\n    identifier: CC-BY-NC-SA-4.0\n\n\nOnly the following fields are required:\n\n\n  title - Your API probably has a name, and if not perhaps now is a good time to think of one thats useful for public consumption.\n  version - The version of your OpenAPI document, which does not have to related to the API version, or the OpenAPI Specification version.\n\n\nUpdating the version number when you make changes is pretty common, and keeping it separate from the API version at first feels a little bit odd, but soon makes sense. After all, you can fix issues with the OpenAPI document that produce no change whatsoever in the API, and vice-versa.\n\nThese other fields are optional:\n\n\n  description - A handy place to put general information, especially introductory topics like where to find access tokens or links to various Postman/Insomnia Collections, or SDKs. This can be done using CommonMark (Markdown) to get as advanced as you want.\n  contact - Help your APIs users find the help they need instead of wandering off to use another API.\n  license - A chance to explain the license details of your API description. Note, that is very different from the license you use for your API source code, which would be licensed through source control with a LICENSE.txt or similar.\n\n\n3. Servers Object\n\nThe servers object specifies one or more server URLs where the API is hosted. Each server can have a URL, a name, and an optional description.\n\nservers:\n- name: Production\n  url: https://api.example.com/v1\n  description: The main production server for the API.\n\n- name: Staging\n  url: https://staging-api.example.com/v1\n  description: A staging server for testing purposes.\n\n\nIt’s fine to put any development and testing servers in here because you can always flag them as internal or strip them out with overlays later.\n\n4. Paths Object\n\nThe paths object is probably the most important section for your API. It lists all the available API endpoints, with each path being a key in the object. Then the object is further broken down by the specific HTTP methods supported by each endpoint. The object in each of these HTTP methods is another object which describes the “operation”, which is a term in OpenAPI to describe a specific combination of path and method.\n\npaths:\n  /bookings:\n    get:\n      operationId: get-bookings\n      summary: List existing bookings\n      tags:\n      - bookings\n      responses:\n        '200':\n          description: A list of bookings                 \n    post:\n      operationId: create-booking\n      summary: Create a booking\n      tags:\n      - bookings\n      requestBody:\n        required: true\n      responses:\n        '201':\n          description: Booking successful\n\n\nHere the operationId helps us spot the two different operations, and give them a unique name which can be useful for all sorts of tools. The summary gives a human readable title that will often be used in documentation tools.\n\nAny HTTP request which has a body (e.g.: POST, PUT, PATCH, QUERY) can define a requestBody, and the responses are broken down by status code. This is a bit of a skeleton at the moment and ignores the media types and payloads.\n\nLearn more about paths &amp; operations.\n\n5. Components Object\n\nThe components object is where various types of reusable objects live. The main thing people use here is schemas, which some people call “data models” but that doesn’t exist anywhere in the specification, thats just a nickname.you might hear.\n\ncomponents:\n  schemas:\n    Trip:\n      type: object\n      properties:\n        id:\n          type: string\n          format: uuid\n          description: Unique identifier for the trip\n        origin:\n          type: string\n          description: The starting station of the trip\n        destination:\n          type: string\n          description: The destination station of the trip\n        departure_time:\n          type: string\n          format: date-time\n          description: The date and time when the trip departs\n        arrival_time:\n          type: string\n          format: date-time\n          description: The date and time when the trip arrives\n\n\nThe schemas defined in components.schemas let you describe common data structures used throughout your API, allowing them to be referenced via $ref whenever a schema is required: whether that is a request body, response body, parameter, or header.\n\ncomponents:\n  requestBodies:\n    TripRequest:\n      description: A request body for creating a new trip.\n      required: true\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Trip'\n\n  responses:\n    TripResponse:\n      description: A single Trip returned as a response.\n      content:\n        application/json:\n          schema:\n            $ref: '#/components/schemas/Trip'\n\n\nComponents can also define parameters which can be used across multiple endpoints:\n\ncomponents:\n  parameters:\n    pageParam:\n      in: query\n      name: page\n      required: false\n      schema:\n        type: integer\n        default: 1\n        description: The page number for pagination.\n\n\nOr common headers that can be returned across multiple endpoints:\n\ncomponents:\n  headers:\n    RateLimit:\n      description: |\n        The RateLimit header communicates quota policies. It contains a `limit` to\n        convey the expiring limit, `remaining` to convey the remaining quota units,\n        and `reset` to convey the time window reset time.\n      schema:\n        type: string\n        examples:\n          - limit=10, remaining=0, reset=10\n\n    Retry-After:\n      description: | \n        The Retry-After header indicates how long the user agent should wait before making a follow-up request. \n        The value is in seconds and can be an integer or a date in the future. \n        If the value is an integer, it indicates the number of seconds to wait. \n        If the value is a date, it indicates the time at which the user agent should make a follow-up request. \n      schema:\n        type: string\n      examples:\n        integer:\n          value: '120'\n          summary: Retry after 120 seconds\n        date:\n          value: 'Fri, 31 Dec 2021 23:59:59 GMT'\n          summary: Retry after the specified date\n\n\nOr examples, so multiple requests, responses, or parameters could share one or more examples.\n\ncomponents:\n  examples:\n    Card:\n      summary: Card Payment\n      value:\n        amount: 49.99\n        currency: gbp\n        source:\n          object: card\n          name: J. Doe\n          number: '4242424242424242'\n          cvc: 123\n          exp_month: 12\n          exp_year: 2025\n          address_line1: 123 Fake Street\n          address_line2: 4th Floor\n          address_city: London\n          address_country: gb\n          address_post_code: N12 9XX\n    Bank:\n      summary: Bank Account Payment\n      value:\n        amount: 100.5\n        currency: gbp\n        source:\n          object: bank_account\n          name: J. Doe\n          number: '00012345'\n          sort_code: '000123'\n          account_type: individual\n          bank_name: Starling Bank\n          country: gb\n\n\nOr securitySchemes which will be called with the security keyword. OpenAPI supports several authentication types, but here are a few examples:\n\ncomponents:\n  securitySchemes:\n    ApiKeyHeader:\n      type: apiKey\n      in: header\n      name: X-API-Key\n\n    BearerToken:\n      type: http\n      scheme: bearer\n\n    JWT:\n      type: http\n      scheme: bearer\n      bearerFormat: JWT\n\n    OAuth2ReadWrite:\n      type: oauth2\n      flows:\n        authorizationCode:\n          scopes:\n            read: Grants read access\n            write: Grants write access\n          authorizationUrl: https://example.com/oauth/authorize\n          tokenUrl: https://example.com/oauth/token\n          refreshUrl: https://example.com/oauth/refresh\n\n\nThis is just a few of the many types of security schemes that can be defined, but defining them alone doesn’t do anything. They need to be referenced by the security object.\n\n6. Security Object\n\nThe top-level security list specifies the security schemes that apply globally to the API, so if an entire API uses an API key or OAuth2 you might have:\n\nsecurity:\n  - apiKey: []\n  - oauth2:\n    - read\n    - write\n\n\nYou can get into path specific overrides and various complex “and” situations with more advanced security functionality.\n\n7. Webhooks Object\n\nwebhooks:\n  newBooking:\n    post:\n      operationId: new-booking\n      summary: New Booking\n      description: |\n        Subscribe to new bookings being created, to update integrations for your users.  Related data is available via the links provided in the request.\n      tags:\n        - bookings\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/Booking'\n      responses:\n        '200':\n          description: Return a 200 status to indicate that the data was received successfully.\n\n\n8. Tags Object\n\nYou may have spotted the tags keyword in the paths and webhooks, and those are referencing tags defined in the top-level tags object. The tag name is used to group related operations together. Each tag needs a name, with optional summary and description.\n\ntags:\n  - name: bookings\n    summary: Bookings\n    description: | \n      Create and manage bookings for train trips, including passenger details\n      and optional extras.\n  - name: payments\n    summary: Payments\n    description: |\n      Pay for bookings using a card or bank account, and view payment\n      status and history.\n\n      &gt; warn\n      &gt; Bookings usually expire within 1 hour so you'll need to make your payment\n      &gt; before the expiry date \n\n\nThe name is more like a variable name so should be camelCase or similar. The summary is short and in human-readable for documentation so its best to make it “Title Case”, and the description is Markdown (CommonMark) which can be quite long - think paragraphs not sentences, explaining what this concept is to the user as that will also show up in most documentation tools.\n\nExample OpenAPI Document\n\nPutting it all together, here is a simple example of an OpenAPI document:\n\nopenapi: 3.2.0\ninfo:\n  title: Sample API\n  description: A sample API to illustrate OpenAPI concepts.\n  version: 1.0.0\n  contact:\n    name: API Support\n    url: http://www.example.com/support\n    email: support@example.com\nservers:\n  - name: Production\n    url: https://api.example.com/v1\n    description: The main production server for the API.\n\npaths:\n  /users:\n    get:\n      summary: List all users\n      responses:\n        '200':\n          description: A list of users\n          content:\n            application/json:\n              schema:\n                type: array\n                items:\n                  $ref: '#/components/schemas/User'\n    post:\n      summary: Create a new user\n      requestBody:\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/User'\n      responses:\n        '201':\n          description: User created\ncomponents:\n  schemas:\n    User:\n      type: object\n      properties:\n        id:\n          type: integer\n          format: int64\n        username:\n          type: string\n        email:\n          type: string\n          format: email\nsecurity:\n  - api_key: []\ntags:\n  - name: users\n    summary: Users\n    description: Manage users in the system.\n\n\nFor a more advanced example, take a look at the Train Travel API, the modern OpenAPI example from Bump.sh."
        },
        {
          "id": "openapi-v3.2-understanding-structure-parameters",
          "title": "Defining Parameters",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/understanding-structure/parameters/",
          "content": "Parameter Types    \n      Path Parameters\n      Query Parameters\n      Header Parameters\n      Cookie Parameters\n    \n  \n  Defining Parameters for Multiple Operations\n  Defining Shared Parameters in Components\n  Reserved Keywords in Parameters\n\n\nParameters in OpenAPI v3.1 are a fundamental part of creating an API specification, allowing you to define the inputs your API can accept.\n\nParameters fall into one of a few types:\n\n\n  Path Parameters: Variables within the path, e.g., /bookings/{bookingId}.\n  Query Parameters: Appended to the URL, e.g., /bookings?date=2024-05-01.\n  Header Parameters: Included in the request header, e.g., Acme-Custom-Header: Value.\n  Cookie Parameters: Passed in the request cookies.\n\n\n\n  In previous versions of OpenAPI the entire request body and form data would all be sent as parameters, but since OpenAPI v3.0 this has been moved to the content object. Learn more in HTTP Requests.\n\n\nEach parameter in OpenAPI is defined with specific attributes such as name, in (location), required, description, and schema (for defining data types and validation rules). Defining parameters with these keywords allows documentation to show example how HTTP requests should be constructed making life easier for the client, but also make sure machines know what to do with it, making SDKs and server-side validation a whole lot more powerful.\n\nParameter Types\n\nPath Parameters\n\nThe first type of parameter to get the hang of is path parameters.\n\n  /bookings/{bookingId}:\n    get:\n      parameters:\n        - name: bookingId\n          in: path\n          required: true\n          description: The ID of the booking to retrieve.\n          schema:\n            type: string\n            format: uuid\n          example: 1725ff48-ab45-4bb5-9d02-88745177dedb\n\n\nHere is one required path parameter, bookingId, with its name matching {bookingId}. The schema can contain anything you’d expect to find in schema, from data types to other validations.\n\n\n  Path parameters have to be marked as required: true because they’re in the path, and if its missing it would break especially if the variable was between two other segments, e.g: /bookings/{bookingId}/payment would become /bookings//payment if the value was empty and that’s going to be confusing.\n\n\nOpenAPI v3.1 is very particular about allowed characters:\n\n\n  The value for these path parameters MUST NOT contain any unescaped “generic syntax” characters described by RFC3986: forward slashes (/), question marks (?), or hashes (#).\n\n\nThis means it’s best to just use normal A-Z and 0-9 characters in the names for your path parameters.\n\nQuery Parameters\n\n  /trips:\n    get:\n      parameters:\n        - name: origin\n          in: query\n          description: The ID of the origin station\n          required: true\n          schema:\n            type: string\n            format: uuid\n          example: efdbb9d1-02c2-4bc3-afb7-6788d8782b1e\n        - name: destination\n          in: query\n          description: The ID of the destination station\n          required: true\n          schema:\n            type: string\n            format: uuid\n          example: b2e783e1-c824-4d63-b37a-d8d698862f1d\n        - name: date\n          in: query\n          description: The date and time of the trip in ISO 8601 format in origin station's timezone.\n          schema:\n            type: string\n            format: date-time\n          example: '2024-02-01T09:00:00Z'\n\n\nIn this example origin, destination, and date are query parameters. The first two are defined as required, because it’s important to know where you’re going from and to when buying a ticket, but the date is optional at this point because a customer might be looking for the cheapest day.\n\nQuery parameters are appended to the URL when a client actually makes the request, e.g., /trips?origin=efdbb9d1-02c2-4bc3-afb7-6788d8782b1e&amp;destination=destination&amp;date=2024-05-01T10:00:00.\n\nHeader Parameters\n\nHeader parameters are sent in the HTTP request as a HTTP header. HTTP headers are often are often used for passing authorization tokens, specifying content types being sent, requesting the types being received, and directing the behavior of cache mechanisms. Some of this is already covered by other OpenAPI functionality so you don’t need to manually re-define Content-Type or Accept, but anything else will need to be defined.\n\nFor example, if you’d like to let API users know they can ask for fresh (uncached) data on a certain endpoint, you can advertise the API respects the If-Modified-Since header like this:\n\npaths:\n  /trips:\n    get:\n      summary: Get train trips\n      parameters:\n        - in: header\n          name: If-Modified-Since\n          schema:\n            type: string\n            format: date-time\n          required: false\n          description: &gt;\n            Allows the client to request the resource only if it has been modified after the specified date and time.\n      responses:\n        '200':\n          content:\n            application/json:\n              schema:\n                type: array\n                items:\n                  $ref: '#/components/schemas/Trips'\n        '304':\n          description: The data has not been modified since the date and time specified in the `If-Modified-Since` header.\n\n\nTry to clearly explain not just what the header does, but in what scenarios a client might want to use it, and focus on how it helps them.\n\nCookie Parameters\n\nCookie parameters are sent in the HTTP request through the Cookies functionality available in all web browsers and some HTTP clients.\n\nCookie parameters can be any primitive values, arrays and objects. Arrays and objects are serialized using the form style. For more information, see Parameter Serialization.\n\nThe first thought might be to use cookie for authentication, but for that you would be better off using API keys. Cookie parameters are reserved for other things, like tracking and analytics, locale preferences, or other session related information which does not fit into the HTTP specification with dedicated headers.\n\npaths:\n  /analytics/visit:\n    get:\n      summary: Track user visit\n      description: Records user visit for analytics purposes.\n      parameters:\n        - name: UserId\n          in: cookie\n          required: false\n          description: Unique user identifier\n          schema:\n            type: string\n            example: \"abc123\"\n        - name: VisitCount\n          in: cookie\n          required: false\n          description: Number of visits by the user\n          schema:\n            type: integer\n            example: 5\n\n\nDefining Parameters for Multiple Operations\n\nAll these examples show parameters being defined at the operation level, but they can also be defined at the path level to avoid repetition. This is especially useful for path parameters, but works for all types of parameters.\n\n  /bookings/{bookingId}:\n    parameters:\n      - name: bookingId\n        in: path\n        required: true\n        description: The ID of the booking to retrieve.\n        schema:\n          type: string\n          format: uuid\n        example: 1725ff48-ab45-4bb5-9d02-88745177dedb\n    get:\n      ...\n    delete:\n      ...\n\n\nBy defining the bookingId parameter at the path level, it will be automatically applied to all operations under the /bookings/{bookingId} path.\n\nDefining Shared Parameters in Components\n\nAlternatively, you can define shared parameters in the components section of your OpenAPI specification. This allows you to reuse the parameters across different paths and operations. Here’s an example:\n\ncomponents:\n  parameters:\n    bookingId:\n      name: bookingId\n      in: path\n      required: true\n      description: The ID of the booking to retrieve.\n      schema:\n        type: string\n        format: uuid\n      example: 1725ff48-ab45-4bb5-9d02-88745177dedb\n\n\nTo use the shared parameter, you can reference it in your path or operation like this:\n\n  /bookings/{bookingId}:\n    get:\n      parameters:\n        - $ref: '#/components/parameters/bookingId'\n    delete:\n      parameters:\n        - $ref: '#/components/parameters/bookingId'\n\n\nThis way, you can maintain consistency and avoid duplicating parameter definitions across your API description.\n\nReserved Keywords in Parameters\n\nSome characters are reserved for use in URI paths, query and header parameters, and this can lead to confusion when you want to use them literally in your parameter values.\n\nOpenAPI allows you to define parameters that can include these reserved characters without percent-encoding them via the allowReserved keyword to path, query, header, and cookie parameters.\n\n\n  The allowReserved keyword only added support for header parameters in OpenAPI v3.2.\n\n\nWhen allowReserved is set to true, the parameter value can include reserved characters without percent-encoding them. This is particularly useful for parameters that need to include characters like /, ?, or # in their values, such as file paths or URLs. For example, / is encoded as %2F (or %2f), so that the parameter value photos/cat.jpg would be sent as:\n\nGET /files?path=photos%2Fcat.jpg\n\n\nThis rules for encoding are known as “reserved expansion”, which is defined by RFC 6570. Basically, it allows RFC 3986’s reserved character set :/?#[]@!$&amp;'()*+,;=, as well as percent-encoded triples. These can all pass through unchanged, but everything else will be percent-encoding (including % outside of percent-encoded triples).\n\nHere’s how allowReserved: true looks in action:\n\nparameters:\n  - in: query\n    name: path\n    schema:\n      type: string\n    allowReserved: true\n\n\nThis allows you to send a request like this without percent-encoding the path:\n\nGET /files?path=photos/cat.jpg\n\n\nLearn more about reserved characters in the OpenAPI v3.2 specification."
        },
        {
          "id": "openapi-v3.2-understanding-structure-paths-operations",
          "title": "Paths and Operations",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/understanding-structure/paths-operations/",
          "content": "Defining Paths\n  HTTP Methods\n  Operations\n\n\nOpenAPI has the concept of “paths” and “operations”, which is two parts of what people would think of as an “endpoint”. The path covers the URL and the operation covers the rest of it.\n\nHere are a list of paths in the Train Travel API example.\n\npaths:\n  /stations:\n  /trips:\n  /bookings:\n  /bookings/{bookingId}:\n  /bookings/{bookingId}/payment:\n\n\nThe path defines the relative path of the API endpoint from wherever the server URL ends, which in this example is https://api.example.com, which together describe full URLs like this:\n\n\n  https://api.example.com/stations\n  https://api.example.com/trips\n  https://api.example.com/bookings\n  https://api.example.com/bookings/{bookingId}\n  https://api.example.com/bookings/{bookingId}/payment\n\n\nPaths can store variables, a little bit like the concept of server variables, using curly braces {} as a placeholder for a parameter which will be defined within the operation.\n\nDefining Paths\n\nEach path can then define one or more operations, using HTTP methods like get, post, put, patch, or delete as a key and the operation as an object inside that.\n\n  /bookings:\n    get:\n      operationId: get-bookings\n      summary: List existing bookings\n      description: Returns a list of all trip bookings by the authenticated user.\n      responses:\n        '200':\n          description: A list of bookings\n          content:\n            application/json:\n              schema:\n                type: array\n                items:\n                  $ref: '#/components/schemas/Booking'\n\n    post:\n      operationId: create-booking\n      summary: Create a booking\n      description: A booking is a temporary hold on a trip. It is not confirmed until the payment is processed.\n      security:\n        - OAuth2:\n            - write\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/Booking'\n      responses:\n        '201':\n          description: Booking successful\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Booking'\n\n\nHTTP Methods\n\nIn OpenAPI v3.1 only the following methods were allowed:\n\n\n  get\n  put\n  post\n  delete\n  options\n  head\n  patch\n  trace\n\n\nOpenAPI v3.2 expanded this list to allow:\n\n\n  query (as defined in draft-ietf-httpbis-safe-method-w-body-08 or later)\n  anything else via “additional properties”.\n\n\nAnything that you are using as a HTTP method could be used in an operation in capitals, so you could use the following examples:\n\n\n  CONNECT - used by proxies to establish a tunnel.\n  COPY - old WebDAV method for copying resources.\n  LOCK - another old WebDAV method for locking a resource to prevent further editing.\n\n\nTempting as it is to use those old WebDAV methods, it’s probably not a great idea, but the fact that OpenAPI v3.2 lets you document any HTTP method you like is certainly an upgrade on previous versions.\n\nOperations\n\nEach operation should have an operationId which is really useful for all sorts of automated tooling, and a summary which is more human-readable and helps the operation show up nicely in documentation tools.\n\nThe description can then be as long and complex as you want, using CommonMark (standardized Markdown) and multi-line YAML syntax to place all the context which cannot be picked up from just looking at variable names.\n\nAny HTTP request which has a body (e.g.: POST, PUT, PATCH, QUERY) can define a requestBody, which can be marked as required or not. Each request can have multiple content types, supporting JSON, XML, CSV, images, whatever you need to define.\n\nA common example would be supporting XML and JSON, but is really helpful for APIs which support image uploads being supported simultaneously via a direct Content-Type: image/* upload, whilst also supporting JSON sending the URL (e.g.: \"image_url\": \"http://...\"). It’s also handy for  “import spreadsheet” type functionality.\n\npaths:\n  /bookings:\n    post:\n      summary: Create a new booking\n      operationId: create-booking\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: '#/components/schemas/Booking'\n          text/csv:\n            schema:\n              type: string\n            example: |\n              departureTime,arrivalTime,operator,price\n              2023-04-01T10:00:00Z,2023-04-01T15:00:00Z,TrainCo,59.99\n      responses:\n        '200':\n          description: Booking created successfully\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/Booking'\n            text/csv:\n              schema:\n                type: string\n              example: |\n                bookingId,departureTime,arrivalTime,operator,price\n                123,2023-04-01T10:00:00Z,2023-04-01T15:00:00Z,TrainCo,59.99\n\n\nThe responses are then broken down by status code, and again all the responses can have multiple content types. Then the content can be further described by a schema, and an example (or examples).\n\nFor both request and response, schema is optional, but is massively helpful and worth putting in the work to define, because this is where all of the HTTP body information exists, which can contain validation rules, potential values, examples, and useful context like “why” and “how” instead of just “what”.\n\n\n  Learn more about defining HTTP requests and HTTP responses.\n  Learn more about schemas and data types."
        },
        {
          "id": "openapi-v3.2-understanding-structure-http-requests",
          "title": "HTTP Requests",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/understanding-structure/http-requests/",
          "content": "Structure of Request Bodies    \n      Creating a Resource\n      Updating a Resource\n    \n  \n  File Uploads &amp; Multipart Forms\n\n\nAny API handling use-cases more advanced that purely fetching data will need to define a HTTP request body. POST, PATCH, PUT, QUERY, etc. all allow a HTTP client to send a body: often JSON or XML. This allows for more information to be sent rather than just query string parameters, which have limits.\n\nThe request body can be used for:\n\n\n  Creating new resources (e.g.: booking a train ticket)\n  Updating existing resources (e.g.: updating that booking)\n  Uploading files (e.g.: uploading an image to your railcard)\n\n\nStructure of Request Bodies\n\nIn OpenAPI 3.x, the request body is defined using the requestBody object. This object allows you to specify:\n\n\n  The content type (e.g.: application/json, application/xml).\n  The schema that defines the structure of the request body.\n  Whether the request body is required or optional.\n  Descriptions for these requests to add context to API documentation.\n\n\nLet’s consider the Train Travel API, which allows users to book train tickets.\n\nCreating a Resource\n\nWhen a user wants to book a train ticket, they need to send details like the passenger’s name, trip ID, date of travel, and seat preference, which would look a bit like this:\n\npaths:\n  /bookings:\n    post:\n      summary: Book a train ticket\n      description: Endpoint to book a train ticket\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              properties:\n                passenger_name:\n                  type: string\n                  example: \"John Doe\"\n                trip_id:\n                  type: string\n                  example: \"1234\"\n                date:\n                  type: string\n                  format: date\n                  example: \"2024-08-15\"\n                seat_preference:\n                  type: string\n                  enum: [window, aisle, any]\n                  example: \"window\"\n\n\nHere the requestBody object defines two important properties:\n\n\n  \n    required: true - indicates that the request body is mandatory for this operation.\n  \n  \n    content - specifies that the request body should be in application/json format with the following schema.\n  \n\n\nThe schema defines the structure of the request body, including properties like passenger_name, train_id, date, and seat_preference. This can be defined inline like this, or it can use components to share an existing schema and reduce repetition.\n\nUpdating a Resource\n\nIf a user wants to update their booking (e.g.: change the seat preference), the API can define a PUT or PATCH operation, to allow updating the entire booking, or part of the booking respectively. Either way, they need to send the updated data in the request body. Here’s how to define it:\n\npaths:\n  /bookings/{bookingId}:\n    patch:\n      summary: Update a booking\n      description: Endpoint to update an existing booking\n      parameters:\n        - name: bookingId\n          in: path\n          required: true\n          schema:\n            type: string\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              properties:\n                seat_preference:\n                  type: string\n                  enum: [window, aisle, any]\n                  examples:\n                  - aisle\n\n\nHere the PATCH method is used to describe an operation that can update one specific field from an existing booking. The required: true says the requestBody is mandatory, and the only media type defined is application/json so that says the request must be in that format.\n\nThe schema then defines the structure of the request body, which demonstrates that only the seat_preference property can be updated.\n\nIf multiple properties could be updated, you would define all the properties that could be updated, then show off some examples for common use-cases of things users might want to do.\n\nFile Uploads &amp; Multipart Forms\n\nHTTP requests can also cover more advanced scenarios like file uploads and multipart form data, which have their own guides in the advanced section."
        },
        {
          "id": "openapi-v3.2-data-models-json-schema",
          "title": "JSON Schema in OpenAPI",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/data-models/json-schema/",
          "content": "JSON Schema Documentation\n  JSON Schema Specifications\n  Extra OpenAPI-specific Properties\n\n\nFor a long time JSON Schema and OpenAPI Schema Objects were similar but different. OpenAPI was inspired by JSON Schema, then they both evolved separately, but finally in OpenAPI v3.1 with lots of work from both teams, the specifications realigned on JSON Schema Draft 2020-12. This means you can learn more about OpenAPI Schema Objects by learning more about JSON Schema.\n\nJSON Schema Documentation\n\nThe best places to start learning about JSON Schema is via the documentation, which comes in a few forms.\n\n\n  JSON Schema Tutorials - Getting started guides, and tutorials focusing in on particular bits like the differences between required and optional properties, nesting schemas, using composition with anyOf, allOf, and oneOf, etc.\n  JSON Schema Glossary - There’s a lot of terminology to wrap your head around depending on how deep you want to go, such as dialect, vocabulary, instance, subschema, composition, etc. This page explains it all and links off to more information on each.\n  Lean JSON Schema - A mixture between specification and tutorial, with lots of examples explaining how various keywords work.\n\n\nThese resources are intended to help the general user not need to go and dive into the specifications straight away, as these are more technical documents aimed more at tooling developers. If you cannot find what you need to know in the documentation then you might end up reading the specifications, so where can those be found?\n\nJSON Schema Specifications\n\nIn OpenAPI v3.2 the Schema Object is defined as a superset of the JSON Schema specification, which is split across two relevant specifications.\n\n\n  JSON Schema Core (IETF draft-bhutton-json-schema-01) - defines the basic foundation of JSON Schema.\n  JSON Schema Validation (IETF draft-bhutton-json-schema-validation-01) - defines the validation keywords of JSON Schema.\n\n\n\n  Unless stated otherwise, the keyword definitions follow those of JSON Schema and do not add any additional semantics; this includes keywords such as $schema, $id, $ref, and $dynamicRef being URIs rather than URLs. Where JSON Schema indicates that behavior is defined by the application (e.g. for annotations), OAS also defers the definition of semantics to the application consuming the OpenAPI document.\nSource: OAS 3.2 Specification\n\n\nIf you end up having to read through the specification to find out something which would fit better in a tutorial, please take the time to contribute that tutorial back to the JSON Schema website to avoid others needing to do the same.\n\nExtra OpenAPI-specific Properties\n\nAs mentioned above the OpenAPI Schema Object is a superset of JSON Schema Draft 2020-12, which means it supports everything and adds a few bits on top. This works because JSON Schema has the concept of dialects, and the OpenAPI Schema Object is a new dialect, which takes the JSON Schema vocabularies that give you all the keywords defined in the core and validation specifications, then adds four more:\n\n\n  example - A free-form property to include an example of an instance for this schema. To represent examples that cannot be naturally represented in JSON or YAML, a string value can be used to contain the example with escaping where necessary. Deprecated: The example property has been deprecated in favor of the JSON Schema examples keyword. Learn more about examples.\n  externalDocs - Additional documentation found elsewhere outside of the OpenAPI or generated documentation, like tutorials or blog posts.\n  xml - Optional keyword for describing XML payloads, which does nothing on root schemas but helps describe properties where there may be wrapping tags or XML attributes.\n  discriminator - The discriminator is an object name that is used to differentiate between other schemas which may satisfy the payload description, acting as a shortcut for oneOf / anyOf. It cannot change the validity of the schema, but it helps tools like code generators work quicker by not having to check every possible schema.\n\n\nMost of this stuff can be ignored to build the majority of APIs, especially if you’re JSON-only. If you do use these keywords do not worry about losing compatibility with JSON Schema tooling. JSON Schema ignores keywords it does not understand, and if any tooling gives you a hard time about these keywords, then not only is it not OpenAPI compliant, it is not JSON Schema compliant either."
        },
        {
          "id": "openapi-v3.2-extending-overlays",
          "title": "Extending OpenAPI Documents with Overlays",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/extending/overlays/",
          "content": "Adding more context with Overlays    \n      Tags\n      Introductory Topics\n      Code Samples\n      External Documentation\n    \n  \n  Summary\n\n\nHowever you make OpenAPI descriptions for your APIs, there are all sorts of scenarios where you might want to customize it for different audiences. Perhaps your tech writers want to add amazing longer descriptions but they are locked out of the source code, or you want to hide some internal endpoints from your OpenAPI before publishing. Is it possible to do all this without awkwardly managing multiple similar-but-different OpenAPI documents?\n\nThe OpenAPI Initiative have released a new concept called “Overlays”. This is separate specification but compatible with OpenAPI, and while it’s labelled “experimental” its a v1.0.0, with support in a variety of tooling including Bump.sh.\n\n# overlays.yaml\noverlay: 1.0.0\ninfo:\n  title: Improve Descriptions\n  version: 0.0.1\nactions:\n  - target: '$.info.description'\n    description: Provide a better introduction for our end users than this techno babble.\n    update: &gt;-\n      Protect Earth's Tree Tracker API will let you see what we've been planting and restoring all\n      around the UK, and help support our work by directly funding the trees we plant or the sites\n      we restore.\n      To get involved [contact us and ask for an access token](https://protect.earth/contact) then\n      [check out the API documentation](https://protect.earth/api).\n\n\nUsing OpenAPI Overlays you can effectively “patch” an OpenAPI description, pointing to parts of the original document with JSONPath, then adding or updating your content in. You can add as many actions to these overlays as you like, or make multiple overlays.\n\nTo work with Overlays you’ll need a tool that understands them, and that’s not all OpenAPI tools as the concept is still very new. Regardless of what API documentation tool you are using, you can use the Bump CLI to apply these overlays, and this will produce a new user-facing document.\n\nbump overlay openapi.yaml overlays.yaml &gt; openapi.public.yaml\n\n\nYou can run these commands in continuous integration, and whatever you would have done with the original you can now do with the new openapi.public.yaml (or whatever you decide to name it).\n\nWhen deploying a document to Bump.sh using the GitHub Action or the CLI, you can skip a step and point the deploy command at the overlay.\n\nname: Deploy documentation\n\non:\n  push:\n    branches:\n      - main\n\njobs:\n  deploy-doc:\n    name: Deploy API doc on Bump.sh\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n\n      - name: Deploy API documentation\n        uses: bump-sh/github-action@v1\n        with:\n          doc: partner-api\n          token: ${{secrets.BUMP_TOKEN}}\n          file: api/openapi.bundle.yaml\n          overlay: api/overlays.yaml\n\n\nLearn more about Overlays and using them within Bump.sh.\n\nAdding more context with Overlays\n\nEngineers will often focus very much on the “how”, but leave out some of the “why”, or really explain the “what”, so if you have an existing OpenAPI document you cannot edit directly, try adding in some of these things with overlays.\n\nTags\n\nTags are a really useful place to explain some of the concepts being used. For example an Order and an Organization might seem fairly obvious what it is to the engineers working on it, but you could add context to them.\n\nHere’s an example of an overlay you could use to expand the tag, adding human-readable summaries to improve navigation in documentation, and adding descriptions with a whole bunch of Markdown to help people find out more information.\n\n# overlays.yaml\noverlay: 1.0.0\ninfo:\n  title: Expand Tag Display Names &amp; Descriptions\n  version: 0.0.1\nactions:\n  - target: '$.tags[?(@.name==\"order\")]'\n    description: Provide more information for order tag.\n    update:\n      summary: Order\n      description: &gt;\n        The Order resource represents a single order for trees, which can be fulfilled by one or more\n        deliveries. Orders are created by the [Protect Earth team](https://protect.earth/contact) and\n        are used to track the progress of your order from creation to delivery.\n  - target: '$.tags[?(@.name==\"organization\")]'\n    description: Provide more information for organization tag.\n    update:\n      summary: Organization\n      description: &gt;\n        The Organization resource represents a single organization, which can be a charity, business,\n        or other entity. Organizations are created by the [Protect Earth team](https://protect.earth/contact)\n        and are connected to each of your Orders.\n\n\nThese descriptions (which can be much longer and full of even more Markdown) will then show up in API Documentation, pride of place, ready to explain the concepts to the user before they get stuck into what specific endpoints are about.\n\n\n\nHere’s the tag description rendered in Bump.sh.\n\nIntroductory Topics\n\nThere are quite a few handy “vendor extensions” around which you can add more power to any tooling that knows how to respond to them. One particularly useful one is x-topics, which allows tech writers (or anyone else messing with this sort of work known as “doc ops” or “spec ops”) to expand on just the API Reference Documentation, and start introducing end-users to other guides and content.\n\nopenapi: 3.2.0\n\nx-topics:\n  - title: Getting started\n    content:\n      $ref: ./docs/getting-started.md\n\n\nIn Bump.sh this will create a new navigation entry, and insert the Markdown content from the reference guide right into the main documentation.\n\n\n\nWhether you inject x-topics with Overlays or directly into OpenAPI in the source code, the result is the same.\n\nCode Samples\n\nThere’s countless other improvements you can make to the source OpenAPI given to you by the engineering teams who have other things to be worrying about, like adding client-side code samples with x-codeSamples.\n\npaths:\n  /users:\n    get:\n      summary: Retrieve a user\n      operationId: getUserPath\n      responses: [...]\n      parameters: [...]\n      x-codeSamples:\n        - lang: ruby\n          label: Ruby library\n          source: |\n            require \"http\"\n             \n            request = HTTP\n              .basic_auth(:user =&gt; \"name\", :pass =&gt; \"password\")\n              .headers(:accept =&gt; \"application/json\")\n             \n            response = request.get(\"https://api.example.com/v1/users\")\n            if response.status.success?\n              # Work with the response.body\n            else\n              # Handle error cases\n            end\n\n\nExternal Documentation\n\nYou could add externalDocs to point them to tutorials hosted elsewhere.\n\ntags:\n  - name: stations\n    summary: Train Stations\n    description: Train Stations all over Europe, using a bunch of standards defined elsewhere.\n    externalDocs:\n      url: https://train-travel.example.com/docs/stations\n\n\nFilter out anything that shouldn’t be there, like beta endpoints that are not ready for public use. There’s a few ways to do this.\n\nBump.sh users can do this with the x-beta property:\n\npaths:\n  /diffs:\n    post:\n      description: Create a diff between any two given API definitions\n      x-beta: true # Beta flag at the operation level\n      requestBody:\n        description: The diff creation request object\n        content:\n          application/json:\n            schema:\n              type: object\n              x-beta: true # Beta flag at the top-level schema object\n              properties:\n                url:\n                  type: string\n                  format: uri\n                  x-beta: true # Beta flag at the object property level\n                  description: |\n                    **Required** if `definition` is not present.\n                    Current definition URL. It should be accessible through HTTP by Bump.sh servers.\n\n\nOr you can filter them out with overlays:\n\noverlay: 1.0.0\ninfo:\n  title: Remove beta flags\n  version: 0.0.1\nactions:\n  - target: \"$..[?(@['x-beta'] == true)]^\"\n    description: Remove anything beta\n    remove: true\n\n\n\n  Learn more about working with JSONPath to write powerful targets for your overlays using our guide How to work with JSONPath.\n\n\nSummary\n\nOverlays are powerful, advanced, and standardized across the toolchain, so you can rely on them to help you with any modifications you need to do.\n\nBeing able to change things however you like, then publish the changed versions off seamlessly is really handy, and will hopefully be the last time you need to do awkward JSON/YAML hacking on other peoples documents. JSONPath is a tricky thing to learn, but if you can master regex you can master JSONPath, then the world is your oyster."
        },
        {
          "id": "openapi-v3.2-advanced-pagination",
          "title": "Pagination",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/advanced/pagination/",
          "content": "Pagination is a common requirement in APIs that return large sets of data. It allows clients to retrieve a subset of a collection, which helps manage the load on servers and improves the user experience by not overwhelming clients with too much data at once.\n\nReturning a subsection of a collection means returning maybe 10, 100, or 1,000 resources, which can be helpful for clients in various circumstances, such as when displaying a list of items in a user interface, or when processing large datasets in smaller chunks.\n\nThis is usually implemented as a query parameter, such as ?page=1 or ?cursor=abc123, but can also be done with HTTP headers or links in the response body. The choice of method depends on the API design and the preferences of the API consumers, so we’ll cover the most common approaches to pagination in OpenAPI.\n\n\n  Query Parameter Pagination    \n      HTTP Example\n      OpenAPI\n    \n  \n  Pagination with Metadata in Response Body    \n      HTTP Example\n      OpenAPI\n    \n  \n  Pagination Using Links    \n      HTTP Example\n      OpenAPI\n    \n  \n  Pagination with HTTP Headers    \n      HTTP Example\n      OpenAPI\n    \n  \n  Cursor-Based Pagination    \n      HTTP Example\n      OpenAPI\n    \n  \n  Best Practices for Pagination in OpenAPI\n\n\nQuery Parameter Pagination\n\nClients specify which “page” of results they want using a page parameter and how many results per page with a limit (or per_page). For example, ?page=2&amp;limit=10 would return the second set of 10 items. The server calculates which items to return based on these values, usually by using offset = (page - 1) * limit.\n\nThis approach is easy to use and works well for stable datasets where items don’t shift around frequently. It’s ideal for user-facing interfaces like tables or product lists. However, because it relies on counting and offsets, it may become inefficient or inaccurate on datasets that change rapidly.\n\nHTTP Example\n\nRequest:\n\nGET /stations?page=2&amp;limit=5 HTTP/1.1\nHost: api.example.com\n\n\nResponse:\n\n{\n  \"data\": [\n    { \"id\": \"6\", \"name\": \"Station 6\" },\n    { \"id\": \"7\", \"name\": \"Station 7\" },\n    { \"id\": \"8\", \"name\": \"Station 8\" },\n    { \"id\": \"9\", \"name\": \"Station 9\" },\n    { \"id\": \"10\", \"name\": \"Station 10\" }\n  ]\n}\n\n\nOpenAPI\n\ncomponents:\n  parameters:\n    Page:\n      name: page\n      in: query\n      description: Page number to retrieve\n      required: false\n      schema:\n        type: integer\n        default: 1\n        minimum: 1\n    Limit:\n      name: limit\n      in: query\n      description: Number of results per page\n      required: false\n      schema:\n        type: integer\n        default: 10\n        minimum: 1\n        maximum: 100\n\npaths:\n  /stations:\n    get:\n      summary: List train stations\n      parameters:\n        - $ref: '#/components/parameters/Page'\n        - $ref: '#/components/parameters/Limit'\n      responses:\n        '200':\n          description: OK\n\n\nPagination with Metadata in Response Body\n\nIn addition to the data, the response includes a meta object that provides information such as the current page, total pages, page size, and total number of results. This gives clients context about the dataset, allowing them to build pagination UIs or navigate intelligently.\n\nThis is often used with query parameter pagination (page and limit) and works especially well when clients need to know how many pages exist or where they are in the dataset. The metadata is part of the JSON response, which makes it self-contained and easy to parse.\n\nHTTP Example\n\nRequest:\n\nGET /stations?page=2&amp;limit=5 HTTP/1.1\nHost: api.example.com\n\n\nResponse:\n\n{\n  \"data\": [\n    { \"id\": \"6\", \"name\": \"Station 6\" },\n    { \"id\": \"7\", \"name\": \"Station 7\" },\n    { \"id\": \"8\", \"name\": \"Station 8\" },\n    { \"id\": \"9\", \"name\": \"Station 9\" },\n    { \"id\": \"10\", \"name\": \"Station 10\" }\n  ],\n  \"meta\": {\n    \"page\": 2,\n    \"size\": 5,\n    \"total_pages\": 10,\n    \"total_items\": 50\n  }\n}\n\n\nOpenAPI\n\ncomponents:\n  schemas:\n    Station:\n      type: object\n      properties:\n        id:\n          type: string\n        name:\n          type: string\n    PaginationMeta:\n      type: object\n      properties:\n        page:\n          type: integer\n        size:\n          type: integer\n        total_pages:\n          type: integer\n        total_items:\n          type: integer\n    PaginatedStationsResponse:\n      type: object\n      properties:\n        data:\n          type: array\n          items:\n            $ref: '#/components/schemas/Station'\n        meta:\n          $ref: '#/components/schemas/PaginationMeta'\n\npaths:\n  /stations:\n    get:\n      responses:\n        '200':\n          description: Paginated stations with metadata\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/PaginatedStationsResponse'\n\n\nPagination Using Links\n\nInstead of asking clients to build pagination URLs, the server includes fully-formed navigation links in the response—typically self, next, and prev. These are provided in a links object and point to the exact URLs clients can follow to navigate pages.\n\nThis approach follows RESTful design principles and is especially useful when the underlying query structure is complex (e.g., filters, sorts, or cursors). Clients don’t need to understand how to build the next URL—they just follow the link provided.\n\nHTTP Example\n\nRequest:\n\nGET /stations?page=2&amp;limit=3 HTTP/1.1\nHost: api.example.com\n\n\nResponse:\n\n{\n  \"data\": [\n    { \"id\": \"4\", \"name\": \"Station 4\" },\n    { \"id\": \"5\", \"name\": \"Station 5\" },\n    { \"id\": \"6\", \"name\": \"Station 6\" }\n  ],\n  \"links\": {\n    \"self\": \"https://api.example.com/stations?page=2&amp;limit=3\",\n    \"next\": \"https://api.example.com/stations?page=3&amp;limit=3\",\n    \"prev\": \"https://api.example.com/stations?page=1&amp;limit=3\"\n  }\n}\n\n\nOpenAPI\n\ncomponents:\n  schemas:\n    PaginationLinks:\n      type: object\n      properties:\n        self:\n          type: string\n          format: uri\n        next:\n          type: string\n          format: uri\n        prev:\n          type: string\n          format: uri\n    PaginatedStationsWithLinks:\n      type: object\n      properties:\n        data:\n          type: array\n          items:\n            $ref: '#/components/schemas/Station'\n        links:\n          $ref: '#/components/schemas/PaginationLinks'\n\npaths:\n  /stations:\n    get:\n      responses:\n        '200':\n          description: Paginated response with links\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/PaginatedStationsWithLinks'\n\n\nPagination with HTTP Headers\n\nIn this method, pagination metadata is moved out of the response body and into custom or standard HTTP headers. Common headers include X-Total-Count, X-Page, X-Per-Page, and the Link header for navigation URLs. The body contains only the raw data, which keeps it clean and minimal.\n\nThis style is common in APIs where response format must be lightweight or consistent across endpoints. It’s also helpful when clients want to inspect pagination information without parsing the body, though it does require clients to handle HTTP headers more deliberately.\n\nHTTP Example\n\nRequest:\n\nGET /stations?page=3&amp;limit=10 HTTP/1.1\nHost: api.example.com\n\n\nResponse Headers:\n\nX-Total-Count: 100\nX-Page: 3\nX-Per-Page: 10\nLink: &lt;https://api.example.com/stations?page=2&gt;; rel=\"prev\",\n      &lt;https://api.example.com/stations?page=4&gt;; rel=\"next\"\n\n\nResponse Body:\n\n[\n  { \"id\": \"21\", \"name\": \"Station 21\" },\n  { \"id\": \"22\", \"name\": \"Station 22\" },\n  { \"id\": \"23\", \"name\": \"Station 23\" }\n]\n\n\nOpenAPI\n\npaths:\n  /stations:\n    get:\n      summary: Get paginated stations with headers\n      responses:\n        '200':\n          description: Paginated results with header metadata\n          headers:\n            X-Total-Count:\n              description: Total number of available items\n              schema:\n                type: integer\n            X-Page:\n              description: Current page number\n              schema:\n                type: integer\n            X-Per-Page:\n              description: Number of items per page\n              schema:\n                type: integer\n            Link:\n              description: Pagination navigation links\n              schema:\n                type: string\n          content:\n            application/json:\n              schema:\n                type: array\n                items:\n                  $ref: '#/components/schemas/Station'\n\n\nCursor-Based Pagination\n\nCursor pagination uses a token (called a “cursor”) to mark the position in a dataset. Instead of using page numbers, the client requests the next chunk of results by passing a cursor from the previous response. For example: GET /items?cursor=abc123. The response includes a next_cursor value to be used in the following request.\n\nThis method is highly efficient and resilient in large or rapidly-changing datasets, especially when items are inserted or deleted often. It avoids issues with data shifting between pages, but it requires the server to maintain and interpret cursors accurately—often based on unique IDs or timestamps.\n\nHTTP Example\n\nRequest:\n\nGET /stations?cursor=c3RhdGlvbjEyMw== HTTP/1.1\nHost: api.example.com\n\n\nResponse:\n\n{\n  \"data\": [\n    { \"id\": \"124\", \"name\": \"Station 124\" },\n    { \"id\": \"125\", \"name\": \"Station 125\" },\n    { \"id\": \"126\", \"name\": \"Station 126\" }\n  ],\n  \"meta\": {\n    \"next_cursor\": \"c3RhdGlvbjEyNg==\",\n    \"has_more\": true\n  }\n}\n\n\nOpenAPI\n\nparameters:\n  - name: cursor\n    in: query\n    required: false\n    description: Cursor for the next set of results\n    schema:\n      type: string\n\ncomponents:\n  schemas:\n    CursorMeta:\n      type: object\n      properties:\n        next_cursor:\n          type: string\n        has_more:\n          type: boolean\n    CursorPaginatedStations:\n      type: object\n      properties:\n        data:\n          type: array\n          items:\n            $ref: '#/components/schemas/Station'\n        meta:\n          $ref: '#/components/schemas/CursorMeta'\n\npaths:\n  /stations:\n    get:\n      parameters:\n        - name: cursor\n          in: query\n          schema:\n            type: string\n      responses:\n        '200':\n          description: Cursor-paginated response\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/CursorPaginatedStations'\n\n\nBest Practices for Pagination in OpenAPI\n\nWhen implementing pagination in your OpenAPI specification, consider the following best practices:\n\n\n  Use $ref for shared pagination metadata and parameters.\n  Be consistent with naming (meta, links, cursor, etc.)\n  Document the pagination method clearly in your API documentation, using operation descriptions and/or topics."
        },
        {
          "id": "openapi-v3.2",
          "title": "OpenAPI 3.2 Specification Complete Guide",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/",
          "content": "OpenAPI is the industry standard for describing HTTP APIs. Maintained by the OpenAPI Initiative under the Linux Foundation, it provides a structured, machine-readable format that tools can use to generate documentation, client libraries, tests, mock servers, and more.\n\nThis guide covers every aspect of the OpenAPI 3.2 specification, from the basics of document structure to advanced topics like streaming, pagination, and overlays.\n\nIntroduction\n\n\n  What is OpenAPI?: what the specification is, what an OpenAPI document looks like, and the key concepts (documents, operations, schemas, security).\n  History and evolution: from Swagger to OpenAPI 3.0, 3.1 (JSON Schema alignment), and 3.2 (streaming, pagination, tag hierarchies).\n  Benefits of using OpenAPI: how OpenAPI accelerates documentation, testing, SDK generation, onboarding, and collaboration across teams.\n\n\nUnderstanding OpenAPI structure\n\nThe core building blocks of every OpenAPI document.\n\n\n  Basic structure: the eight main sections of an OpenAPI document: version, info, servers, paths, components, security, webhooks, and tags.\n  API servers: defining base URLs for different environments (dev, staging, production) with support for server variables and dynamic substitution.\n  Paths and operations: describing endpoints and HTTP methods (GET, POST, PUT, DELETE, and the new QUERY method in v3.2) with request bodies and responses.\n  Parameters: path, query, header, and cookie parameters with types, validation, and reusability via components.\n  Parameter serialization: how arrays and objects are encoded in URLs using styles like form, spaceDelimited, pipeDelimited, and deepObject.\n  HTTP requests: defining request bodies with content types, schemas, and required/optional flags.\n  HTTP responses: documenting status codes, response headers, content types, and empty responses.\n  Components: reusable schemas, parameters, responses, request bodies, headers, examples, and security schemes referenced with $ref.\n\n\nDefining data models\n\n\n  Schemas and data types: types (string, number, integer, boolean, array, object, null), formats (date-time, email, uuid), validation keywords (enum, minimum, maximum, pattern), and readOnly/writeOnly.\n  JSON Schema in OpenAPI: the relationship between OpenAPI and JSON Schema Draft 2020-12, and the four additional OpenAPI-specific keywords.\n  Examples and defaults: providing sample values at schema, media type, and parameter levels for documentation and mock servers.\n  Schema composition: combining schemas with allOf (AND), anyOf (OR), and oneOf (XOR) for polymorphism and inheritance.\n  Representing XML: customizing XML element names, attributes, namespaces, and array wrapping with the xml keyword.\n\n\nAdvanced topics\n\n\n  Splitting documents with $ref: managing large API definitions across multiple files and URLs for better collaboration and reusability.\n  Multipart form data: describing form submissions that combine text fields, files, and metadata in a single request.\n  Pagination: query parameters, response metadata, link-based, header-based, and cursor pagination strategies.\n  File uploads: direct binary uploads and multipart uploads with base64 encoding and content type negotiation.\n  Multiple content types: serving JSON, XML, CSV, and other formats from a single endpoint.\n  Error formats: standardized error responses with RFC 9457 Problem Details, JSON:API errors, and reusable error schemas.\n  Security: authentication schemes (API keys, HTTP bearer, OAuth2, OpenID Connect, mutual TLS) applied globally or per-operation with scopes.\n  Callbacks and webhooks: describing asynchronous events sent by the API to clients, including operation callbacks and standalone webhooks.\n  JSON streaming: describing streamed responses (JSONL, NDJSON, Server-Sent Events) with the new itemSchema keyword in v3.2.\n\n\nDocumenting APIs\n\n\n  Descriptions and summaries: writing clear summaries and descriptions for operations, parameters, tags, and responses that improve developer experience.\n  Grouping with tags: organizing endpoints into logical sections with descriptions, ordering, and the new tag hierarchies in v3.2.\n  External documentation: linking to tutorials, guides, and knowledge bases from operations, tags, and schemas.\n\n\nExtending OpenAPI\n\n\n  Specification extensions: adding custom x-* properties for vendor-specific features (code samples, beta flags, feedback links) without breaking compatibility.\n  Overlays: non-destructively modifying OpenAPI documents using JSONPath targeting to add descriptions, hide internal endpoints, or customize for different audiences.\n\n\nWorkflow and best practices\n\n\n  The perfect modern OpenAPI workflow: using OpenAPI as a single source of truth in Git, with automated linting, contract testing, documentation deployment, mock servers, and SDK generation.\n\n\nQuick reference\n\n\n  The Cheat Sheet: a downloadable one-page visual reference of the entire specification for quick lookup."
        },
        {
          "id": "openapi-v3.2-introduction-what-is-openapi",
          "title": "What is OpenAPI?",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/introduction/what-is-openapi/",
          "content": "Concepts\n  OpenAPI structure\n  Format    \n      OpenAPI Specification\n      OpenAPI Document\n      OpenAPI Documentation\n    \n  \n\n\nOpenAPI (the “OpenAPI Specification”) is a standard for describing an API. OpenAPI is managed by the OpenAPI Initiative (OAI). The OpenAPI Specification (OAS) defines an open and independent description format for HTTP API services, which allows both humans and computers to discover and understand how an API works and how to interact with it, without the need to look at the source code.\n\nOpenAPI provides a machine-readable structured data format which can be also be read and written by people, allowing for tooling to help API developers, API product managers, technical writers, and governance teams, all the way through the API lifecycle.\n\n\n\nDiagram created by OpenAPI Initiative.\n\nConcepts\n\nAs with anything in computing there’s lots of terminology and some definitions vary or overlap, which can be a bit confusing. Here is a quick reminder to understand correctly every concept and call a cat a cat.\n\n\n\nOpenAPI structure\n\nYour OpenAPI documents lets you describe your REST API:\n\n\n  Define general information about your API: description, terms of use, license, contact, etc…\n  Authentication methods HTTP, API keys, OAuth 2, OpenID, etc…\n  Available endpoints /users, etc…\n  Since OpenAPI 3.1, available webhooks\n  Available operations on each endpoint: GET, POST, PUT, PATCH, DELETE, etc…\n  Input and output parameters for each operation\n\n\nFormat\n\nOpenAPI documents can be written in YAML or JSON formats.\n\nThese formats were chosen because they are easy for a human to read and write, and easy for machines to parse. In practice, YAML is the most used format adopted to write OpenAPI documents. Like it or not, YAML is easier to read than JSON mainly because it reduces the use of markup tags. Also, it is a format that is widely used to write any sort of software configuration.\n\nHere is an example of a partial OpenAPI document covering one endpoint, written in YAML:\n\n/previews:\n  post:\n    summary: Create a preview\n    description: |\n      Create a preview for a given documentation file. The preview will have a unique\n      temporary URL, and will be active for 30 minutes.\n    security: []\n    requestBody:\n      $ref: \"#/components/requestBodies/Preview\"\n    responses:\n      \"201\":\n        description: \"Success\"\n        content:\n          \"application/json\":\n            schema:\n              $ref: \"#/components/schemas/Preview\"\n\n\nOpenAPI Specification\n\nAlso known as OpenAPI spec / OAS\n\nThe “OpenAPI Specification” describes the specification written and maintained by the OpenAPI Initiative, and published on spec.openapis.org. This is a technical document that helps OpenAPI users and tooling vendors have one set of expectations about how things should work.\n\nOpenAPI Document\n\nAlso known as OpenAPI documents / OpenAPI file / OpenAPI description / OpenAPI contract\n\nAn OpenAPI document describes how your API works, or how it will work when it’s been built, and it’s written following the OpenAPI specification. Think of this like a blueprint for your API. While an “API description” is a vague concept, OpenAPI Document is pretty concrete, it’s where the OpenAPI that describes your API lives, and is usually something like openapi.yaml or openapi.json.\n\nOpenAPI Documentation\n\nAlso known as API Reference\n\nWhen you have an OpenAPI document one of the main things people do with it is create API documentation, more specifically “API Reference Documentation”, which is human-readable technical documentation showing an end user all the relevant information about endpoints, requests, responses, etc. Your documentation can be automatically generated from your OpenAPI document to avoid the pain of writing it by hand."
        },
        {
          "id": "openapi-v3.2-advanced-json-streaming",
          "title": "JSON Streaming",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/advanced/json-streaming/",
          "content": "JSON Streaming\n  Streaming with OpenAPI    \n      itemSchema\n      itemEncoding\n    \n  \n  Popular Streaming Formats    \n      JSON Lines &amp; NDJSON\n      JSON Text Sequence\n      Server-Sent Events (SSE)\n    \n  \n  Sentinel Events\n  Further Reading\n\n\nStreaming data allows API servers to send and receive data in real-time or in\nchunks, rather than waiting for the entire response to be ready. This is already\nhow browsers handle HTML, images, and other media, and now it can be done for\nAPIs working with JSON.\n\nThis can improve responses with lots of data, or be used to send events from\nserver to client in realtime without polling or adding the complexity of\nWebhooks or WebSockets. Streaming works by sending “chunks”, which clients can\nthen work with individually instead of waiting for the entire response to be\nready.\n\nStreaming JSON in particular is increasingly useful as expectations around big\ndata, data science, and AI continue to grow. JSON on its own does not stream\nvery well, but a few standards and conventions have popped up to expand JSON\ninto a streamable format, and OpenAPI v3.2 introduces keywords to describe data\nin these stream formats.\n\nJSON Streaming\n\nStreaming JSON is a bit tricky because JSON is not designed to be streamed. A\nnaive approach might look like this:\n\n{\n  {\"timestamp\": \"1985-04-12T23:20:50.52Z\", \"level\": 1, \"message\": \"Hi!\"},\n\n\nThis would trip up most tooling, but we can use something like JSON\nLines (a.k.a JSONL) to send one JSON instance per line.\n\n{\"timestamp\": \"1985-04-12T23:20:50.52Z\", \"level\": 1, \"message\": \"Hi!\"}\n{\"timestamp\": \"1985-04-12T23:20:51.37Z\", \"level\": 1, \"message\": \"Hows it hangin?\"}\n{\"timestamp\": \"1985-04-12T23:20:53.29Z\", \"level\": 1, \"message\": \"Bye!\"}\n\n\nThis format allows each line to be a valid JSON object, making it easy to parse\nwith standard native tooling and a for loop. There are a bunch of other\nstreaming formats you might want to work with in your API like Newline\nDelimited JSON (NDJSON), JSON Text\nSequence, GeoJSON Text\nSequence. Thankfully they are\nall quite similar and working with them in OpenAPI is almost identical.\n\nStreaming with OpenAPI\n\nOpenAPI v3.0 &amp; v3.1 were able to stream binary data, but struggled to support\nJSON streaming formats as there was no standard way to define the schema of\nindividual events in a stream. People would try to describe things as an\narray:\n\ncontent:\n  application/jsonl:\n    schema:\n      type: array\n      items: \n        type: object\n        properties:\n          timestamp:\n            type: string\n            format: date-time\n          level: \n            type: integer\n          message:\n            type: string\n\n\nYou might see this sort of thing around, but it’s not valid, and will confuse\ntooling. A stream cannot be described as a single array, and it is a sequence of\nmultiple objects on new lines which is rather different. Some tools could spot\nthe application/jsonl content type and figure that out, but we don’t need\nawkward hacks anymore because the OpenAPI team have solved the problem.\n\nOpenAPI v3.2 introduces two new keywords to describe streamed data and events:\n\n\n  itemSchema - define the structure of each item in a stream.\n  itemEncoding - define how those items are encoded (or serialized), as text, JSON, binary, etc.\n\n\nitemSchema\n\nDescribing a stream with itemSchema works just like schema with one\ndifference: it will be applied to each item in the stream, instead of the entire\nresponse.\n\nConsider an example like the train travel API running a stream of tickets:\n\nHTTP/1.1 200 OK\nX-Powered-By: Express\nContent-Type: application/jsonl; charset=utf-8\nTransfer-Encoding: chunked\nDate: Tue, 19 Aug 2025 18:36:10 GMT\nConnection: keep-alive\nKeep-Alive: timeout=5\n\n{\"train\":\"ICE 123\",\"from\":\"Berlin\",\"to\":\"Munich\",\"price\":79.9}\n{\"train\":\"TGV 456\",\"from\":\"Paris\",\"to\":\"Lyon\",\"price\":49.5}\n{\"train\":\"EC 789\",\"from\":\"Zurich\",\"to\":\"Milan\",\"price\":59}\n\n\nTo describe this stream of items, we can use the itemSchema keyword:\n\ncontent:\n  application/jsonl:\n    itemSchema:\n      type: object\n      properties:\n        train:\n          type: string\n        from:\n          type: string\n        to:\n          type: string\n        price:\n          type: number\n          format: float\n\n\nTooling now has two important switches it can use to figure out how to handle\nthe response. The itemSchema makes it clear the response is a stream, and the\napplication/jsonl content type lets tooling decide how to present that.\n\nFor streaming formats that just handle streams of JSON, the itemSchema is\noften sufficient to describe the structure of each item in the stream. For more\ncomplicated formats, additional encoding information may be needed.\n\nitemEncoding\n\nThe itemEncoding keyword allows you to specify how each item in the stream\nshould be encoded, with the same encoding object as the encoding keyword.\n\nUsing itemEncoding is only possible for multipart/* responses, so it is not\nvery useful for an API that’s streaming JSON, unless you were streaming a\nmixture of JSON and assets/docs/images on a single response.\n\ncontent:\n  multipart/mixed:\n    itemSchema:\n      $comment: A single data image from the device\n    itemEncoding:\n      contentType: image/jpg\n\n\nLet’s ignore itemEncoding for now and focus on the major use case of streams for\nAPIs: streaming data and events.\n\nPopular Streaming Formats\n\n\n  JSON Lines\n  NDJSON\n  JSON Text Sequences\n  Server-Sent Events (SSE)\n\n\nThey all work a little different, but they share the common goal of allowing\ndata to be sent in a continuous stream rather than as a single, complete\nresponse.\n\nJSON Lines &amp; NDJSON\n\nWorking with JSON Lines or NDJSON is basically identical in OpenAPI, and feels\nvery much like working with plain JSON responses just with a different header\nand a bit of itemSchema usage.\n\nIf using JSONL use content type application/jsonl, and if using NDJSON use\ncontent type application/x-ndjson.\n\npaths:\n  /logs:\n    get:\n      summary: Stream of logs as JSON Lines\n      responses:\n        '200':\n          description: |\n            A stream of JSON-format log messages that can be read\n            for as long as the application is running, and is available\n            in any of the sequential JSON media types.\n          content:\n            application/jsonl:\n              itemSchema:\n                type: object\n                properties:\n                  timestamp:\n                    type: string\n                    format: date-time\n                  level:\n                    type: integer\n                    minimum: 0\n                  message:\n                    type: string\n              examples:\n                JSONL:\n                  summary: Log entries\n                  description: JSONL examples are. just a string where each line is a valid JSON object.\n                  value: |\n                    {\"timestamp\": \"1985-04-12T23:20:50.52Z\", \"level\": 1, \"message\": \"Hi!\"}\n                    {\"timestamp\": \"1985-04-12T23:20:51.37Z\", \"level\": 1, \"message\": \"Hows it hangin?\"}\n                    {\"timestamp\": \"1985-04-12T23:20:53.29Z\", \"level\": 1, \"message\": \"Bye!\"}\n\n\nThe example once again shows JSONL as a series of JSON objects with a newline character \\n (0x0A) between them. This can only be described as a YAML multiline string, because JSONL/NDJSON cannot be described as plain JSON/YAML due to the newline characters.\n\n\n  Remember to use value: |, because the pipe will allow newlines to be passed through. USing value: &gt; would remove newlines and put each JSON instance onto the same line.\n\n\nThe sample code for either of these formats could look a bit like this:\n\napp.get(\"/tickets\", async (_, res) =&gt; {\n  res.setHeader(\"Content-Type\", \"application/jsonl; charset=utf-8\");\n  res.setHeader(\"Transfer-Encoding\", \"chunked\");\n\n  for (const ticket of tickets) {\n    res.write(JSON.stringify(ticket) + \"\\n\");\n  }\n\n  res.end();\n});\n\n\nJSON Text Sequence\n\nA third JSON streaming format which would be identical other than a weird little complication. The other two formats are just a newline character \\n (0x0A) at the end of the line, but RFC 7464: JSON Text Sequence requires a control character at the start ASCII Record Separator (0x1E). This is not a visible character in most contexts, but it will be in there like this:\n\n0x1E{\"timestamp\": \"1985-04-12T23:20:50.52Z\", \"level\": 1, \"message\": \"Hi!\"}\n0x1E{\"timestamp\": \"1985-04-12T23:20:51.37Z\", \"level\": 1, \"message\": \"Hows it hangin?\"}\n0x1E{\"timestamp\": \"1985-04-12T23:20:53.29Z\", \"level\": 1, \"message\": \"Bye!\"}\n\n\nThe 0x1E (ASCII Record Separator) indicates the start of a new JSON object in the stream. Control characters are a bit magical and invisible to most text editors so it can be a little confusing. Working with JSON Text Sequence tooling for both producing the stream and reading the stream can solve this problem, letting the tooling insert and read out the control characters without you needing to worry.\n\nimport { Generator } from \"json-text-sequence\";\n\n// ... snip express setup ...\n\napp.get(\"/tickets\", async (_, res) =&gt; {\n  res.setHeader(\"Content-Type\", \"application/json-seq\");\n\n  const g = new Generator();\n  g.pipe(res);\n\n  for (const ticket of tickets) {\n    g.write(ticket);\n  }\n\n  res.end();\n});\n\n\nThe json-text-sequence package makes this easier and provides a simple method for generating and consuming JSON Text Sequences.\n\nServer-Sent Events (SSE)\n\nStreaming JSON as chunks of data is only one way that JSON gets streamed. What about sending events, with some JSON being passed along as attributes?\n\nServer-Sent Events (SSE) can handle this, as a standard for sending real-time updates from a server to a client over HTTP. In OpenAPI, you can define SSE streams using the text/event-stream content type and the itemSchema keyword to describe the structure of the events being sent.\n\ncontent:\n  description: A request body to add a stream of typed data.\n  required: true\n  content:\n    text/event-stream:\n      itemSchema:\n        type: object\n        properties:\n          event:\n            type: string\n          data:\n            type: string\n          retry:\n            type: integer\n        required: [event]\n        # Define event types and specific schemas for the corresponding data\n        oneOf:\n        - properties:\n            event:\n              const: addString\n        - properties:\n            event:\n              const: addInt64\n            data:\n              format: int64\n        - properties:\n            event:\n              const: addJson\n            data:\n              contentMediaType: application/json\n              contentSchema:\n                type: object\n                required: [foo]\n                properties:\n                  foo:\n                    type: integer\n\n\nThe oneOf is optional, but a handy use of polymorphism to describe different schemas for each event - which can really help with documentation and validation.\n\nValid events to come through this stream might look like:\n\nevent: addString\ndata: This data is formatted\ndata: across two lines\nretry: 5\n\nevent: addInt64\ndata: 1234.5678\nunknownField: this is ignored\n\nevent: addJSON\ndata: {\"foo\": 42}\n\n\nSentinel Events\n\nSome streaming systems do not always send all data or events in the exact same way. The items in a stream could be polymorphic objects, or there could be some special events that come through to say the stream is closed (also known as sentinel events).\n\nInstead of trying handle all of these edge cases with special new keywords, OpenAPI allows you to use the standard JSON Schema keywords to model these variations.\n\ntext/event-stream:\n  itemSchema:\n    oneOf:\n    - &lt;your normal data/event schema&gt;\n    - const: { data: \"[DONE]\" }\n\n\nWhatever the schema is, it can be defined using the standard JSON Schema keywords like oneOf, anyOf, or allOf to handle variations in the event structure. This allows you to define a flexible schema that can accommodate different types of events in the stream.\n\nFurther Reading\n\nYou can find details in the OpenAPI Specification v3.2.0 under Media Types Object and look for itemSchema."
        },
        {
          "id": "openapi-v3.2-data-models-representing-xml",
          "title": "Representing XML",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/data-models/representing-xml/",
          "content": "The XML Object\n  Examples of the XML Object    \n      Changing the Element Name\n      Using Namespaces\n      Using Attributes\n    \n  \n\n\nOpenAPI is set up with the assumption that you’re most likely describing JSON, because that’s what over 80% of APIs are using, but XML is still in the game and you could be using both in the same API. OpenAPI supports this with the xml keyword, which helps when XML output is using XML-specific syntax like attributes and wrapped arrays.\n\nBy combining schema composition and references, it’s possible to create reusable components that be used for both JSON and XML output, like this simplistic example below.\n\nopenapi: 3.2.0\ninfo:\n  title: Representing XML\n  description: An API that supports advanced XML\n  version: 1.0.0\npaths:\n  /stations:\n    get:\n      description: Get a list of train stations\n      operationId: get-stations\n      responses:\n        '200':\n          description: OK\n          content:\n            application/xml:\n              schema:\n                $ref: '#/components/schemas/Stations'\ncomponents:\n  schemas:\n    Stations:\n      type: array\n      items:\n        $ref: \"#/components/schemas/Station\"\n\n    Station:\n      type: object\n      xml:\n        name: stations\n      properties:\n        id:\n          type: string\n          format: uuid\n          examples:\n          - b2e783e1-c824-4d63-b37a-d8d698862f1d\n        name:\n          type: string\n          description: The name of the station\n          examples:\n          - Paris Gare du Nord\n        address:\n          type: string\n          examples:\n          - 18 Rue de Dunkerque 75010 Paris, France\n        country_code:\n          type: string\n          format: iso-country-code\n          examples:\n          - FR\n\n\nThis OpenAPI would be used to describe XML data that looked like this:\n\n&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;Station xmlns=\"http://example.com/xml/namespace\"&gt;\n  &lt;id&gt;b2e783e1-c824-4d63-b37a-d8d698862f1d&lt;/id&gt;\n  &lt;name&gt;Paris Gare du Nord&lt;/name&gt;\n  &lt;address&gt;18 Rue de Dunkerque 75010 Paris, France&lt;/address&gt;\n  &lt;country_code&gt;FR&lt;/country_code&gt;\n&lt;/Station&gt;\n\n\nWhen using $ref and components like this, most tools will grab the tag name from the $ref, which is how we got Stations.\n\nIf you want to control that name, you can set the xml.name keyword on the schema, and change it.\n\ncomponents:\n  schemas:\n   Stations:\n      type: array\n      items:\n        $ref: \"#/components/schemas/Station\"\n\n    Station:\n      type: object\n      xml:\n        name: station\n      properties:\n        # ...\n\n\n&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;station&gt;\n\t&lt;id&gt;b2e783e1-c824-4d63-b37a-d8d698862f1d&lt;/id&gt;\n\t&lt;name&gt;Paris Gare du Nord&lt;/name&gt;\n\t&lt;address&gt;18 Rue de Dunkerque 75010 Paris, France&lt;/address&gt;\n\t&lt;country_code&gt;FR&lt;/country_code&gt;\n&lt;/station&gt;\n\n\nBetter! Lowercase looks better, now lets get it wrapped in a tag so we can have multiple using the xml.wrapped property.\n\ncomponents:\n  schemas:\n    Stations:\n      type: array\n      xml:\n        name: data\n        wrapped: true\n      items:\n        $ref: \"#/components/schemas/Station\"\n\n    Station:\n      type: object\n      xml:\n        name: station\n\n\nThat looks like this:\n\n&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;data&gt;\n\t&lt;station&gt;\n\t\t&lt;id&gt;b2e783e1-c824-4d63-b37a-d8d698862f1d&lt;/id&gt;\n\t\t&lt;name&gt;Paris Gare du Nord&lt;/name&gt;\n\t\t&lt;address&gt;18 Rue de Dunkerque 75010 Paris, France&lt;/address&gt;\n\t\t&lt;country_code&gt;FR&lt;/country_code&gt;\n\t&lt;/station&gt;\n&lt;/data&gt;\n\n\nNow the XML looks a bit more ready for action, but if that’s not enough to get your API and OpenAPI on the same page about your XML structure there are plenty of keywords to play with:\n\nThe XML Object\n\nInside the xml object you can use any of these keywords:\n\n\n  name: (string) Replaces the name of the element/attribute used for the described schema property. When defined within items, it will affect the name of the individual XML elements within the list. When defined alongside type being array (outside the items), it will affect the wrapping element and only if wrapped is true. If wrapped is false, it will be ignored.\n  namespace: (string) The URI of the namespace definition. This MUST be in the form of an absolute URI.\n  prefix: (string) The prefix to be used for the name.\n  attribute: (boolean) Declares whether the property definition translates to an attribute instead of an element. Default value is false.\n  wrapped: (boolean) MAY be used only for an array definition. Signifies whether the array is wrapped (for example, &lt;books&gt;&lt;book/&gt;&lt;book/&gt;&lt;/books&gt;) or unwrapped (&lt;book/&gt;&lt;book/&gt;). Default value is false. The definition takes effect only when defined alongside type being array (outside the items).\n\n\nExamples of the XML Object\n\nHere are a few examples of how the xml object can be used in OpenAPI.\n\nChanging the Element Name\n\nYou can use the name keyword to change the name of a single property too. for example changing the name of the id property in XML only.\n\ncomponents:\n  schemas:\n    Station:\n      type: object\n      properties:\n        id:\n          type: string\n          format: uuid\n          xml:\n            name: stationId\n        ...\n\n\nThis will result in the following XML:\n\n&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;station&gt;\n  &lt;stationId&gt;b2e783e1-c824-4d63-b37a-d8d698862f1d&lt;/stationId&gt;\n  ...\n&lt;/station&gt;\n\n\nUsing Namespaces\n\nIf you need to define a namespace for your XML elements, you can use the namespace keyword. Here’s an example:\n\ncomponents:\n  schemas:\n    Stations:\n      type: array\n      xml:\n        name: data\n        namespace: http://example.com/xml/namespace\n        wrapped: true\n        # ...\n\n\nThe resulting XML will include the namespace declaration:\n\n&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;data xmlns=\"http://example.com/xml/namespace\"&gt;\n\t&lt;station&gt;\n    ...\n\n\nUsing Attributes\n\nTo define an XML attribute instead of an element, you can use the attribute keyword. Here’s an example:\n\ncomponents:\n  schemas:\n    Stations:\n      type: array\n      xml:\n        name: data\n        wrapped: true\n      items:\n        $ref: \"#/components/schemas/Station\"\n\n    Station:\n      type: object\n      xml:\n        name: station\n      properties:\n        id:\n          type: string\n          format: uuid\n          xml:\n            attribute: true\n        # ...\n\n\nThis will result in the following XML, where the id is now an attribute:\n\n&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;data&gt;\n\t&lt;station id=\"b2e783e1-c824-4d63-b37a-d8d698862f1d\"&gt;\n\t\t&lt;name&gt;Paris Gare du Nord&lt;/name&gt;\n\t\t&lt;address&gt;18 Rue de Dunkerque 75010 Paris, France&lt;/address&gt;\n\t\t&lt;country_code&gt;FR&lt;/country_code&gt;\n\t&lt;/station&gt;\n&lt;/data&gt;\n\n\nThese are just a few examples of how you can use the xml object in OpenAPI to customize the representation of XML data. Explore all the options and see how it looks in various API documentation tools until you get the hang of it."
        },
        {
          "id": "openapi-v3.2-data-models-schema-composition",
          "title": "Schema Composition",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/data-models/schema-composition/",
          "content": "What are oneOf, anyOf, and allOf?    \n      oneOf\n      anyOf\n      allOf\n    \n  \n  References in Composition\n\n\nIn OpenAPI v3.1 and JSON Schema, you can use oneOf, allOf, and anyOf keywords to handle composition, which is the concept of combining multiple schemas and subschemas in various ways to handle polymorphism, or “extending” other schemas to add more criteria.\n\nWhat are oneOf, anyOf, and allOf?\n\n\n  allOf: (AND) Must be valid against all of the subschemas.\n  anyOf: (OR) Must be valid against any of the subschemas.\n  oneOf: (XOR) Must be valid against exactly one of the subschemas.\n\n\nAll of these keywords must be an array, where each item is a schema. Be careful with recursive schemas as they can exponentially increase processing times.\n\noneOf\n\nThe oneOf keyword is used when you want to specify that a value should match one of the given schemas exactly. It’s useful when you have different possible data structures or types for a particular field, like accepting bank account or card payments, or having train tickets and tram tickets, which are similar but a little different.\n\nThe validation will pass if the value matches exactly one of the schemas defined in oneOf.\n\nThis can be done for a single value:\n\nproperties:\n\ttimestamp:\n    oneOf:\n    - type: string\n      format: date-time\n      examples:\n      - '2024-07-21T17:32:28Z'\n    - type: integer\n      examples: \n      - 1721820298\n\n\nIn this example the timestamp property could be either a RFC 3339 date time (e.g. 2017-07-21T17:32:28Z) or a unix timestamp (e.g. 1721820298).\n\nThat shows how it works for a single property, but oneOf can also be used with whole objects:\n\n properties:\n    source:\n      oneOf:\n        - title: Card\n          properties:\n            number:\n              type: string\n            cvc:\n              type: integer\n            exp_month:\n              type: integer\n              format: int64\n            exp_year:\n              type: integer\n              format: int64\n          required:\n            - number\n            - cvc\n            - exp_month\n            - exp_year\n        \n        - title: Bank Account\n          type: object\n          properties:\n            number:\n              type: string\n            sort_code:\n              type: string\n            account_type:\n              type: string\n              enum:\n                - individual\n                - company\n          required:\n            - number\n            - account_type\n\n\n\nIn the above example, the source property will be an object either way, but the properties contained within can be in one of two combinations. Either number, cvc, exp_month, and exp_year are valid and in good form (a card payment), or  number, sort_code, and account_type will be sent (a bank account). These are the only two outcomes which will return a valid result in a validator, because if neither subscheme match it will be invalid, and if multiple subschemas match that will also be invalid.\n\nanyOf\n\nThe anyOf keyword is very similar to oneOf but a little less restrictive. oneOf is more like a XOR in programming, where one or the other can match, but never both. anyOf is more like a regular OR, which allows one or another or both.\n\nJust like oneOf, you can use anyOf when you have multiple valid options for a particular field. The validation will pass if the value matches one or more of the listed subschemas.\n\nanyOf:\n  - type: number\n    multipleOf: 5\n  - type: number\n    multipleOf: 3\n\n\nThe values 1, 2, 4, 7, 8, 11, 13, 14 would all be rejected for not being multiples of either 3 or 5.\n\nThe values 3, 5, 6, 9, 10, 12 would be valid for being multiples of 3 or 5.\n\nThe value 15 is valid because it is multiples of both 3 and 5, and anyOf is fine with that.\n\nallOf\n\nThe allOf keyword is used when you want to specify that a value should match all of the given schemas. It is useful when you want to combine multiple schemas together. The validation will pass if the value matches all of the schemas defined in allOf.\n\nallOf:\n  - type: object\n    properties:\n      name:\n        type: string\n  - type: object\n    properties:\n      age:\n        type: integer\n        minimum: 0\n\n\nIn the above example, the value should be an object that has both a name property of type string and an age property of type integer with a minimum value of 0.\n\nReferences in Composition\n\nAll of these keywords can contain a list of subschemas that are defined directly inside them, or a $ref can point to a schema defined elsewhere.\n\nschema:\n  oneOf:\n    - $ref: '#/components/schemas/Card'\n    - $ref: '#/components/schemas/BankAccount'\n\n\nThis says that the schema can be either one of these schemas stored as shared components.\n\nDue to the way allOf works, you can essentially reference multiple schemas and say “I want all of the validation rules and criteria from all of these schemas to apply here”, providing a sort of merge-like functionality.\n\nschema:\n  allOf:\n    - $ref: '#/components/schemas/PaymentMethod'\n    - $ref: '#/components/schemas/BankAccount'\n\n\nThis basically declares a schema which has a lot of generic payment fields, then adds specific fields from the bank account type, to avoid declaring generic fields like “name” and “number” in both.\n\nFeel free to mix and match a $ref and an inline subschema, which is a handy way to pop some extra content into a generic shared schema like HATEOAS links:\n\ncontent:\n  application/json:\n    schema:\n      allOf:\n        - $ref: '#/components/schemas/Booking'\n        - properties:\n            links:\n              $ref: '#/components/schemas/Links-Self'\n\n\nThese schema composition keywords provide flexibility and allow you to define complex data structures and validation rules in OpenAPI v3.1 and JSON Schema, which becomes more useful as you start to improve reuse across one or more API."
        },
        {
          "id": "openapi-v3.2-introduction-benefits",
          "title": "OpenAPI Benefits",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/introduction/benefits/",
          "content": "API Design-First\n  Code-first\n  Tools for OpenAPI    \n      Editors\n      Linters\n      Documentation\n      Mocking\n      Testing\n      Clients generator\n      Observability\n    \n  \n  Give it a try\n\n\nOpenAPI is a game-changer for saving your team time and money. By automating routine tasks like creating detailed API documentation, generating client libraries and generating chunks of server-side code, even automating the checking of the API against style guides, it frees up your developers to focus on more important work. This means faster development and fewer hours spent on repetitive coding, which saves time and money as better quality APIs can be delivered quicker.\n\nIt also makes collaboration smoother and more efficient. With OpenAPI, you have a clear, consistent description of your API that everyone can follow. This reduces misunderstandings and miscommunications between different teams—whether they’re front-end, back-end, or QA. Fewer mix-ups mean less time fixing errors and more time building great features.\n\nWhen it comes to testing and validation, OpenAPI shines by enabling automated testing against your API specifications. This catches bugs early in the development process, which is cheaper and easier to fix than issues found later on. Reliable, bug-free APIs lead to happy users and less downtime, saving you from costly fixes and lost customers.\n\nOnboarding new developers is made a lot more efficient with OpenAPI. The detailed documentation helps new team members or customers quickly understand how your APIs work, learning about key validation rules, cutting down on the “time to first request” by sharing sample HTTP requests in curl or code samples in various programming languages. Faster onboarding means new hires can start contributing sooner, and customers can start paying for use sooner.\n\nAPI Design-First\n\nIn an API design-first world, OpenAPI allows you to describe your whole API from endpoints to examples before even writing the first line of code.\n\nUsing this approach, OpenAPI becomes the cornerstone of your API, and becomes the single source of truth in your organization. Code is based on what has been validated during the design phase, and the documentation is generated and synced with the OpenAPI document by deploying updated docs when new commits are merged.\n\nYour team can collaborate at every step of the API design phase and leverage their workflow:\n\n\n  \n    Business and product teams can specify new features that meet consumers needs and a technical writer or an engineer can create or update the OpenAPI documents. Teams can discuss the changes, test the impacts and validate them.\n  \n  \n    The API design process is boosted: frontend and backend developers can use the OpenAPI file to start working on the implementation, even if this is not the final version of the document.\n  \n\n\nCode-first\n\nObviously, we can’t talk about API Design-First without mentioning the previous popular approach of Code-First, as it can have some benefits as well.\n\nIf you need to deploy an API fast for a MVP, internal use or with few endpoints, spending time on API design before you start coding may not be necessary and may slow your delivery time.\n\nAs developers, we have our rooted habits and Code-First follows the historical development process. We put ourselves directly into coding, without the need to learn yet another language or design tools to create our APIs. Sometimes it is a great time saver.\n\nTools for OpenAPI\n\nThere are many tools to help you get the most out of OpenAPI, at every step of the API life cycle, here is a selection of our preferred ones:\n\nEditors\n\n\n  OpenAPI-GUI\n  Stoplight Studio\n  Swagger Editor\n  Insomnia\n\n\nLinters\n\n\n  Spectral\n  Vacuum\n\n\nDocumentation\n\n\n  Bump.sh 💙\n  Swagger UI\n  Redoc\n  Readme\n\n\nMocking\n\n\n  Microcks\n  Prism\n  Wiretap\n\n\nTesting\n\n\n  Microcks\n  Postman\n\n\nClients generator\n\n\n  OpenAPI Generator\n\n\nObservability\n\n\n  Akita\n  Optic\n\n\nBesides the ones mentioned above, here is an amazing and more exhaustive list of curated tools for OpenAPI: https://openapi.tools/\n\nGive it a try\n\nNow that you know what OpenAPI is, try it out with one of the following OpenAPI documents.\n\n\n  Train Travel API\n  Bump API\n\n\nOr why not learn to make your own, heading over to the next section: Understanding the Structure of OpenAPI."
        },
        {
          "id": "openapi-v3.2-introduction-history",
          "title": "A brief history of OpenAPI",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/openapi/v3.2/introduction/history/",
          "content": "Major differences between OpenAPI 2.0, 3.0, 3.1    \n      Versions\n      Structural changes\n    \n  \n\n\nThe OpenAPI Specification started off life with another name, and this can cause a bit of confusion. Until version 3.0, the specification was still called “Swagger”, before being renamed to “OpenAPI” in 2016. It’s actually a “retroactive” rename, so even v2.0 and earlier are called OpenAPI now.\n\nThe OpenAPI Specification is now supervised by the OpenAPI Initiative, an open-source project under the Linux Foundation.\n\nThe name Swagger is still popular, and many of the tools have the word Swagger in, but generally speaking you are better off searching for “OpenAPI tools” than “Swagger tools” because those are mostly old outdated tools which don’t work with modern versions of OpenAPI.\n\nMajor differences between OpenAPI 2.0, 3.0, 3.1\n\nVersions\n\nIn the 2.0 specification, a property called swagger indicated which version of the specification you are using. In OpenAPI 3.0, this is replaced by a new openapi property:\n\n\n  swagger: \"2.0\" line is thus transformed into openapi: \"3.0.0\"\n\n\nStructural changes\n\nThe following image sums up the main structural changes between 2.0 and 3.0. As you can see, a simplification effort has been made to group each concern in a more logical way.\n\n\n\nIf you want to get more into the details about what changed between OpenAPI 3.0 and 3.1, you can have a look at our dedicated article here.\n\nThe changes between version 3.1 and 3.2 also have their own article."
        },
        {
          "id": "arazzo-v1.0-cheatsheet",
          "title": "Arazzo 1.0 - The Cheat Sheet",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/arazzo/v1.0/cheatsheet/",
          "content": "Now that you’ve discovered Arazzo through the complete guide, the Cheat Sheet will come in handy. It highlights the key elements to keep in mind when creating a new Arazzo workflow or maintaining existing ones.\n\n\nDownload the PDF version\n\nHere are the different sections:\n\n  Why Arazzo\n  Document structure\n  General information\n  Sources\n  Reuse components\n  Workflow structure\n  Inputs\n  Steps\n  Outputs\n  Runtime (step execution, runtime expressions, success and failure)\n\n\nFeel free to contribute to future versions of it! You can open issues to request changes directly in our public repository."
        },
        {
          "id": "arazzo-v1.0-introduction-what-is-arazzo",
          "title": "What is Arazzo?",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/arazzo/v1.0/introduction/what-is-arazzo/",
          "content": "Previous approaches to API workflows\n  Introducing Arazzo\n  What Arazzo looks like\n  Who benefits from Arazzo?\n\n\nArazzo (the “Arazzo Specification”) is a standard format for describing API workflows. Managed by the OpenAPI Initiative (OAI), the folks that brought you OpenAPI. Arazzo provides a way to define sequences of API calls that work together to accomplish workflows.\n\nWhile OpenAPI describes what an API can do in any given interaction, Arazzo describes how those interactions can be chained together to achieve real-world goals, like finding the right train ticket, picking a seat, and booking it.\n\nPrevious approaches to API workflows\n\nFor years OpenAPI has made “API reference documentation” considerably better, going beyond the hand-crafted written stuff that rarely had any useful details and was usually out of date. API clients are able to integrate with APIs much quicker, and it’s helped at every stage of the API lifecycle from design, testing, monitoring, and back to designing new versions again.\n\nThis API reference documentation provides detailed information about specific bits of the API’s interface, like all the endpoints, the parameters, request &amp; response bodies, status codes, content types, etc. That’s really handy stuff, even more so if there are no SDKs built for an API, but it does not help anyone understand how to actually use these API endpoints together to accomplish a real task.\n\nSome HTTP APIs are treated like a data source, with CRUD operations being completely independent of each other, but REST APIs are most commonly used to define workflows. Using our Train Travel API example, a typical user journey to book a ticket could involve a whole bunch of steps:\n\n\n  Search for stations in a particular area.\n  See available trips between origin and destination stations, on a certain date, and with a dog!\n  Pick a seat on that train.\n  Create a booking for that trip and seat.\n  Pay for the booking.\n  Get a ticket issued for the paid booking.\n\n\nEach step depends on data from previous steps (like available trips depend on the chosen stations and date), and there are many ways things can go wrong (no trips found, seat already booked, payment failed, etc).\n\nUntil Arazzo, most of the advice for teams maintaining API documentation was to document these workflows using one of the following approaches:\n\n\n  Written documentation - Technical writers will create written explanations with examples of workflows, but these explanations and examples can quickly become outdated, and they cannot be automatically tested.\n  Sample codebases - These are language specific which can cause confusion for developers not familiar with them, and cannot be reused for other automated workflows.\n  Proprietary implementations - Tools like Postman and Readme offer proprietary ways to handle this which can lead to vendor lock-in, limiting integration potential, and are limited in expressing complex logic.\n  Custom scripts - Useful for testing but not standardized or portable.\n\n\nAll of these approaches will partially solve the problem for some aspect of what various teams need, but just like OpenAPI solved the problem of API contracts being rewritten over and over in different formats for different documentation, testing, and governance tools, a solution was needed to standardize API workflow documentation and reduce the repetition that was so prone to mistakes and mismatches.\n\nIntroducing Arazzo\n\nArazzo was designed to solve all these problems by creating a standardized, machine-readable, vendor-neutral description format to describe API workflows. Instead of picking a single problem sector (e.g. documentation) they brought in authors and contributors from a variety of backgrounds to create a specification that can be used for any part of the API lifecycle.\n\nMuch like OpenAPI did for endpoint documentation, Arazzo standardizes how we describe the multi-step journeys so that tooling can parse them, validate them, and even visualize them. It also fits neatly into the ecosystem you already have, because Arazzo doesn’t replace OpenAPI: it extends it by using OpenAPI documents as sources for the API operations your workflows call.\n\narazzo: 1.0.0\ninfo:\n  title: Train Travel API - Book &amp; Pay\n  version: 1.0.0\nsourceDescriptions:\n  - name: train-travel\n    url: ./openapi.yaml\n    type: openapi\nworkflows:\n  - ...\n\n\nThese workflow steps reference “operations” (API calls) from those sources.\n\nworkflows:\n  - workflowId: book-trip\n    summary: Find train trips to book between origin and destination stations.\n    inputs:\n      $ref: \"#/components/inputs/book_trip_input\"\n    steps:     \n      - stepId: book-trip\n        operationId: create-booking\n\n\nTo turn this into brilliant documentation it needs some descriptions, which tools can use to generate user-friendly guides. Beyond that, the best part of this workflow documentation is visualizing the messiest part of real integrations: carrying values from one step to the next.\n\nArazzo is designed to allow inputs and outputs from each step to be clearly described and passed between each other, then branching based on what happened, deciding success or failure based on more than “did I get a 2xx?”, and handling retries or recovery paths when something goes wrong.\n\nBecause the whole thing is structured, the workflows can be executed. That means the same file can power testing and automation, not just documentation. And once the documentation is executable there is no way for documentation and implementation to drift out of sync, because you can run the workflows as tests to verify everything still works as expected on every single pull request or commit to main branch.\n\nWhat Arazzo looks like\n\nHere’s a simple example of an Arazzo workflow for searching and booking a train:\n\narazzo: 1.0.1\ninfo:\n  version: 1.0.0\n\nsourceDescriptions:\n  - name: train-travel\n    url: ./openapi.yaml\n    type: openapi\n\nworkflows:\n  - workflowId: book-train-ticket\n    summary: Search for and book a train ticket\n    steps:\n      - stepId: search\n        operationId: search-trips\n        parameters:\n          - name: origin\n            in: query\n            value: $inputs.origin\n          - name: destination\n            in: query\n            value: $inputs.destination\n        successCriteria:\n          - condition: $statusCode == 200\n        outputs:\n          tripId: $response.body#/trips/0/id\n\n      - stepId: createBooking\n        operationId: create-booking\n        requestBody:\n          contentType: application/json\n          payload:\n            tripId: $steps.search.outputs.tripId\n            passengers: $inputs.passengers\n        successCriteria:\n          - condition: $statusCode == 201\n        outputs:\n          bookingId: $response.body#/id\n\n\nThis workflow defines two steps: searching for trips and creating a booking. It specifies how to pass parameters and handle outputs between steps, so it’s clear which bits come from where, and clearly defining what success looks like: e.g: this API uses a 201 Created status code, where some poorly designed APIs might use 200 OK for everything, or a 202 Accepted for async operations.\n\nThis clarity allows tools to generate accurate documentation, run tests, and even automate these workflows reliably.\n\nWho benefits from Arazzo?\n\n\n  API designers - Document intended usage patterns during the design phase\n  Backend developers - Validate that implementations support the designed workflows\n  QA engineers - Use workflows as integration test suites\n  Technical writers - Generate accurate workflow documentation automatically\n  API consumers - Understand how to accomplish real tasks, not just individual calls\n  DevOps teams - Use workflows for smoke tests and monitoring\n\n\nArazzo brings the same revolution to API workflow documentation that OpenAPI brought to API reference documentation: a single source of truth that serves multiple purposes throughout the API lifecycle."
        },
        {
          "id": "arazzo-v1.0-understanding-structure-components-and-references",
          "title": "Components & references",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/arazzo/v1.0/understanding-structure/components-and-references/",
          "content": "The components object\n  Component types    \n      Inputs\n      Parameters\n      Success actions\n      Failure actions\n    \n  \n  Referencing components    \n      Inputs\n      Actions        \n          Success actions\n          Failure actions\n          Conditional action reuse\n        \n      \n    \n  \n  Summary\n\n\nArazzo helps teams and tools not waste time and effort writing and parsing the same stuff over and over again. By defining reusable components just once, they can be reused in various parts of the workflow. This not only makes it quicker to write them and cuts down on the maintenance burden, but far more importantly it drastically reduces the chance of human error that can cause inconsistencies.\n\nReuse in Arazzo is built upon two concepts: the components object, and the reference keyword.\n\nThe components object\n\nThe components object at the root of your Arazzo document contains reusable definitions:\n\narazzo: 1.0.1\ninfo:\n  title: My Workflows\n  version: 1.0.0\n\ncomponents:\n  inputs:\n    # Reusable input schemas\n  \n  parameters:\n    # Reusable parameters\n  \n  successActions:\n    # Reusable success actions\n  \n  failureActions:\n    # Reusable failure actions\n\nworkflows:\n  # ... workflows can reference components\n\n\nComponent types\n\nArazzo supports several component types, each designed to reduce repetition and make workflows easier to maintain.\n\nInputs\n\ncomponents:\n  inputs:\n    pagination:\n      type: object\n      properties:\n        page:\n          type: integer\n          minimum: 1\n          default: 1\n        pageSize:\n          type: integer\n          minimum: 1\n          maximum: 100\n          default: 20\n    \n    dateRange:\n      type: object\n      required: [startDate, endDate]\n      properties:\n        startDate:\n          type: string\n          format: date\n        endDate:\n          type: string\n          format: date\n    \n    authentication:\n      type: object\n      required: [username, password]\n      properties:\n        username:\n          type: string\n        password:\n          type: string\n          format: password\n\n\nThis example defines three input objects, named pagination, dateRange, and authentication. The latter two have required properties, whilst the former has only optional properties that can helpfully define defaults using the JSON Schema default keyword.\n\nThis is a good place to focus on human descriptions explaining what each input is for, and any constraints not covered by the schema itself.\n\nParameters\n\nParameters bring an input value and introduce it to the world of HTTP. They define how to send data to the source API, whether in the path, query string, headers, or cookies.\n\n\n  path - Part of the URL itself, using OpenAPI-style path templating. For example, in /items/{itemId}, the path parameter is itemId.\n  query - Appended to the URL as a query string, like /items?id=123.\n  header - Sent as an HTTP header. Header field names are case-insensitive (see RFC 9110: Field Names).\n  cookie - Sent as a cookie value to the source API.\n\n\nA parameter is considered unique by the combination of its name and in fields, so give it a sensible name and don’t have two parameters with the same name in the same location.\n\ncomponents:\n  parameters:\n    authHeader:\n      name: Authorization\n      in: header\n      value: 'Bearer {$inputs.token}'\n    \n    acceptJson:\n      name: Accept\n      in: header\n      value: application/json\n\n    itemId:\n      name: id\n      in: query\n      value: $inputs.itemId\n\n\nSuccess actions\n\nSuccess actions define reusable actions to take when a step completes successfully.\n\nSuccessful actions have two possible outcomes, they can either end the workflow there, or they can go to another step.\n\nWhichever the outcome, defining criteria is an optional way to decide if that outcome should be taken. The criteria objects rely on runtime expressions, and can be paired with Regex, JSONPath, JSON Pointers, or XPath for even more advanced logic.\n\ncomponents:\n  successActions:\n    paymentPending:\n      name: paymentPending\n      type: goto\n      stepId: getBooking\n      criteria:\n        - condition: $response.body#/status == 'pending'\n\n    paymentSucceeded:\n      name: paymentSucceeded\n      type: end\n      criteria:\n        - context: $response.body\n          condition: $.status == 'succeeded'\n          type: jsonpath\n\n\nBoth of those conditions are actually the same but using different expression types. The first uses a simple expression, while the second uses JSONPath to extract the status from the response body.\n\nLearn more about success and failure actions in the Success and failure guide.\n\nFailure actions\n\nFailure actions define reusable actions to take when a step fails.\n\ncomponents:\n  failureActions:\n    retryOnServerError:\n      name: serverErrorRetry\n      type: retry\n      retryAfter: 5\n      retryLimit: 3\n      criteria:\n        - condition: $statusCode &gt;= 500\n    \n    handleRateLimit:\n      name: rateLimited\n      type: retry\n      retryAfter: 60\n      retryLimit: 5\n      criteria:\n        - condition: $statusCode == 429\n    \n    logAndEnd:\n      name: logFailure\n      type: goto\n      stepId: logError\n\n\nReferencing components\n\nNow that these components are defined, they can be referenced using the reference keyword.\n\ncomponents:\n  parameters:\n    authHeader:\n      name: Authorization\n      in: header\n      value: 'Bearer {$inputs.token}'\n\nworkflows:\n  - workflowId: myWorkflow\n    steps:\n      - stepId: authenticatedCall\n        operationId: $sourceDescriptions.api.getUser\n        parameters:\n          - reference: $components.parameters.authHeader\n\n\nReferenced parameters are pulled into the step at runtime, just as if they had been defined inline.\n\nYou can even mix referenced and inline definitions.\n\nsteps:\n  - stepId: search\n    operationId: $sourceDescriptions.api.search\n    parameters:\n      - reference: $components.parameters.authHeader\n      - name: query\n        in: query\n        value: $inputs.searchTerm\n      - name: limit\n        in: query\n        value: 20\n\n\nHere the authHeader parameter is reused as-is, while query and limit are defined inline for this one step.\n\nInputs\n\nInputs are a little different than the rest of Arazzo in that they are defined entirely using JSON Schema. This means there is little space for the reference keyword, but that’s no trouble as JSON Schema comes with its own $ref keyword.\n\nWhile JSON Schema composition keywords like allOf are valid, they are not always supported consistently by Arazzo tooling. In practice, the most widely supported pattern is to define full input schemas under components.inputs, then point workflows[].inputs at them with $ref.\n\ncomponents:\n  inputs:\n    pagination:\n      type: object\n      properties:\n        page:\n          type: integer\n          default: 1\n        pageSize:\n          type: integer\n          default: 20\n      \nworkflows:\n  - workflowId: listBookings\n    inputs:\n      type: object\n      properties:\n        pagination:\n          $ref: '#/components/inputs/pagination'\n        status:\n          type: string\n          enum: [pending, confirmed, cancelled]\n  \n  - workflowId: listUsers\n    inputs:\n      type: object\n      properties:\n        pagination:\n          $ref: '#/components/inputs/pagination'\n        role:\n          type: string\n          enum: [admin, user, guest]\n\n\nEach input schema can define properties, and use $ref to pull in reusable input components mixed in with inline definitions for just that workflow.\n\nActions\n\nActions are where reference really shines: define consistent behavior once, then attach it to any step using onSuccess or onFailure.\n\nSuccess actions\n\nUse a success action component when multiple steps should react the same way to success.\n\nFor example, using the paymentPending and paymentSucceeded success actions defined earlier, you can attach both outcomes to a payment step.\n\nworkflows:\n  - workflowId: completeBooking\n    steps:\n      - stepId: payForBooking\n        operationId: $sourceDescriptions.api.createBookingPayment\n        onSuccess:\n          - reference: $components.successActions.paymentPending\n          - reference: $components.successActions.paymentSucceeded\n\n\nBecause each success action has criteria, you can attach both and let runtime data decide whether the workflow continues (goto) or completes (end).\n\nFailure actions\n\nFailure actions are often reused even more heavily, because retry logic and error routing tends to be consistent across many steps.\n\nworkflows:\n  - workflowId: completeBooking\n    steps:\n      - stepId: payForBooking\n        operationId: $sourceDescriptions.api.createBookingPayment\n        onFailure:\n          - reference: $components.failureActions.retryOnServerError\n          - reference: $components.failureActions.handleRateLimit\n          - reference: $components.failureActions.logAndEnd\n\n\nThis step reuses the failure actions from earlier: retry on transient server errors, retry more cautiously when rate limited, and then route to a logging step if it still fails.\n\nConditional action reuse\n\nCriteria let you attach multiple actions to the same step and still get different outcomes based on runtime data.\n\nworkflows:\n  - workflowId: completeBooking\n    steps:\n      - stepId: payForBooking\n        operationId: $sourceDescriptions.api.createBookingPayment\n        onSuccess:\n          - reference: $components.successActions.paymentPending\n          - reference: $components.successActions.paymentSucceeded\n        onFailure:\n          - reference: $components.failureActions.retryOnServerError\n          - reference: $components.failureActions.handleRateLimit\n\n\nBecause each action has criteria, you can attach them together without them all firing every time.\n\nSummary\n\nArazzo provides powerful reusability through:\n\n\n  Components object for shared definitions\n  reference for Arazzo-style references (simple, readable)\n  External references for cross-file reuse\n  Component types: inputs, parameters, success actions, failure actions\n\n\nInputs are JSON Schema, so you may see $ref in workflow inputs and inside input schemas. That’s JSON Schema’s own referencing mechanism and is separate from Arazzo’s reference keyword.\n\nEffective use of components and references makes your Arazzo documents more maintainable, consistent, and easier to understand. Next, let’s explore how workflows are executed at runtime."
        },
        {
          "id": "arazzo-v1.0-introduction-history",
          "title": "History and evolution of Arazzo",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/arazzo/v1.0/introduction/history/",
          "content": "The birth of Arazzo    \n      Version 1.0 release (2024)\n    \n  \n  Adoption and ecosystem\n  The future\n\n\nThe Arazzo Specification represents years of evolution in API workflow documentation, emerging from real-world needs and community collaboration to become an official standard under the OpenAPI Initiative.\n\nThe birth of Arazzo\n\nThe first time Arazzo came up was on an OpenAPI community call somewhere in late 2021 where a bunch of the usual OpenAPI contributors were discussing the Special Interest Groups that could be formed under the OpenAPI Initiative. Along with groups for Overlays, Security, Travel, etc. one of the groups was Workflows. The idea was to explore how to standardize the way API workflows were defined.\n\nThere had been some loose atempts to do this with OpenAPI’s Links where one operation can point to next potential steps, but this was not being utilized much and seemed problematic for trying to document complex workflows across multiple APIs.\n\nPopular open-source tools like Strest immediately jumped to mind for many of us, and there was more inspiration pulled from all sorts of similar concepts:\n\n\n  GitHub Actions and other CI/CD workflow formats (steps, conditions, outputs).\n  State machine concepts from computer science.\n  Real-world API integration patterns observed in the field.\n\n\nGenerally the folks involved were well used to working with these sorts of tools, building and maintaining these sorts of tools, and had seen the pain points of trying to document workflows in written guides or sample codebases that quickly became outdated, so everyone got some good ideas into the mix.\n\nIn 2022 a formal proposal was drafted and presented to the OpenAPI Initiative, and a working group was formed to develop the specification further. This group included API designers, technical writers, tool developers, and other stakeholders who contributed their expertise to shape the emerging standard.\n\nThe specification was initially called “OpenAPI Workflows” but was later renamed to Arazzo to give it a distinct identity while maintaining its connection to the OpenAPI ecosystem. The name “Arazzo” (Italian for “tapestry”) reflects how workflows weave together multiple API calls into a cohesive pattern.\n\nVersion 1.0 release (2024)\n\nAfter extensive community review, tooling experiments, and real-world testing, Arazzo 1.0.0 was officially released in 2024 as a standard under the OpenAPI Initiative. This first version includes:\n\n\n  Core workflow structure - Documents, workflows, and steps\n  Source descriptions - Referencing OpenAPI and other API definitions\n  Runtime expressions - Passing data between steps\n  Success and failure criteria - Defining what constitutes success or failure\n  Parameters and request bodies - Overriding or supplementing source definitions\n  Components and reusability - Reducing duplication through references\n  Outputs - Capturing and passing results between steps\n\n\nAdoption and ecosystem\n\nArazzo has seen growing interest across the API community with most tooling vendors busily working on adding support, with the first two being Spectral and Speakeasy. The list of Arazzo-compatible tools continues to grow, and for the most up-to-date list check OpenAPI.Tools: Arazzo.\n\nThe future\n\nArazzo 1.1 is already underway with non-breaking improvements potentially including:\n\n\n  Support for JSONPath as well as JSON Pointer and XPath.\n  Supporting AsyncAPI for event-driven workflows.\n  Allow workflow inputs to be passed in success and failure actions.\n\n\nThese iterative improvements aim to bring more flexibility and power to the specification while maintaining backward compatibility, whilst larger improvements are already being planned for a longer term Arazzo 2.0 release.\n\nIf you’re interested in contributing or learning more, check out the Arazzo GitHub repository."
        },
        {
          "id": "arazzo-v1.0-introduction-benefits",
          "title": "Benefits of using Arazzo",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/arazzo/v1.0/introduction/benefits/",
          "content": "Documentation benefits\n  Testing benefits\n  Design, governance, and operations\n  Cross-API orchestration\n  Business value\n\n\nHTTP/REST APIs generally are not just data stores, they usually act more like state machines moving users/clients through various workflows. Despite this, most APIs are designed, documented, and tested as if every request is independent of everything else.\n\nREST APIs specifically attempted to solve this problem through HATEOAS (Hypermedia As The Engine Of Application State) being a core principle allowing clients to navigate through application states via links provided in responses. The trend never really caught on at large, but the problem of helping clients navigate through APIs has never been fully solved. This has lead to API clients generally stumbling around in the dark, hoping there is some manually maintained documentation explaining how to move from one state to another, with the occasional code sample thrown in to help. Some of this might even be up-to-date.\n\nIn a world where agentic-AI is becoming more prevalent, this problem is only getting worse. Instead of human developers stumbling around in the dark, we now have AI agents fumbling around trying to figure out how to use APIs to accomplish tasks on behalf of users. These agents need clear, unambiguous instructions on how to navigate through API workflows, including handling errors and edge cases.\n\nDefining these workflows outside of the API itself helps provide clarity to this classic problem, avoiding confusing the client by shoving state controls into the runtime data models and headers. Instead they can be defined in a separate document that references the API definitions, describing how to use them together to accomplish real-world tasks.\n\nThe Arazzo Specification provides a standardized way to define these API workflows, making them machine-readable and executable, and the way it extends OpenAPI means the schemas are right there for humans and agents to work with, with the schema and validation rules helping to cut down on hallucinations.\n\nUsing Arazzo not only solves the documentation problem, but also drastically improves the process of design, governance, end to end testing, chaos testing, and all sorts of operational challenges like monitoring and health checks.\n\nDocumentation benefits\n\nAn API client needs to do a thing: book a ticket, onboard a user, move an order from “draft” to “paid”, or recover when something fails halfway through.\n\nThere probably is not a POST /moveOrderFromDraftToPaid endpoint (and if there is there shouldn’t be). Instead there are a series of steps that need to be taken in order, with data flowing from one step to the next, and various failure modes that need to be handled along the way.\n\nArazzo can be used to capture the journey step-by-step, with the same kind of clarity OpenAPI brought to individual operations. When you write a workflow with good descriptions and sensible outputs, you’ve effectively created a living “how to use this API” guide that tools can render, validate, and even execute.\n\nHere’s what that kind of story looks like, at a high level:\n\nsequenceDiagram\n  autonumber\n  participant Client\n  participant API\n\n  Client-&gt;&gt;API: Search trips\n  API--&gt;&gt;Client: trips[] (includes tripId)\n  Client-&gt;&gt;API: Create booking (tripId)\n  API--&gt;&gt;Client: bookingId\n  Client-&gt;&gt;API: Pay for booking (bookingId)\n  API--&gt;&gt;Client: confirmation + ticket\n\n\nAnd here’s the same idea expressed as an Arazzo workflow snippet. Ignore the syntax of some of the runtime expressions for now, the key part is how the steps flow together with outputs from one step being used as inputs to the next:\n\nworkflows:\n  - workflowId: complete-booking-flow\n    summary: Test the full booking process\n    steps:\n      - stepId: search\n        operationId: search-trips\n        successCriteria:\n          - condition: $statusCode == 200\n          - context: $response.body\n            condition: $[?count(@.trips) &gt; 0]\n            type: jsonpath\n        outputs:\n          tripId: $response.body#/trips/0/id\n\n      - stepId: book\n        operationId: create-booking\n        requestBody:\n          payload:\n            tripId: $steps.search.outputs.tripId\n        successCriteria:\n          - condition: $statusCode == 201\n          - condition: $response.body#/status == 'confirmed'\n        outputs:\n          bookingId: $response.body#/id\n\n      - stepId: pay\n        operationId: pay-booking\n        requestBody:\n          payload:\n            bookingId: $steps.book.outputs.bookingId\n            payment:\n              mean: credit_card\n              details: NDExMTExMTExMTExOjAxMzE6MTExCg==\n        successCriteria:\n          - condition: $statusCode == 201\n          - condition: $response.body#/status == 'payment_succeeded'\n        outputs:\n          ticketNumber: $response.body#/ticket/number\n          ticketAztecCode: $response.body#/ticket/aztecCode\n\n\nIf you’ve ever maintained getting started documentation, you’ll know the pain: they’re the first thing users read and the first thing that goes stale. Arazzo helps because you can treat workflows as the canonical version of those journeys, then generate docs and examples from the same source.\n\nCommon workflows could involve:\n\n\n  log in\n  getting a user id number\n  using that user id to find resources\n  creating new resources\n  knowing if that worked or failed\n\n\nIt seems like it should be pretty standard stuff, but every API is different and these journeys are often surprisingly confusing to navigate without clear documentation that’s definitely up-to-date.\n\nTesting benefits\n\nOnce workflows are written down in a machine-readable format, testing stops being a separate project. The workflow itself becomes the test: run it against staging in CI, run it as a smoke test before deploy, or run it periodically as monitoring.\n\nWhere Arazzo gets especially interesting is state. Many APIs behave like state machines, but they’re documented as if every request is independent. Arazzo gives you a place to express state transitions as something deliberate and testable.\n\nHere’s the idea in the abstract:\n\nstateDiagram-v2\n  [*] --&gt; draft\n  draft --&gt; submitted: submit\n  submitted --&gt; processing: pay\n  processing --&gt; shipped: ship\n  shipped --&gt; delivered: confirmDelivery\n  delivered --&gt; [*]\n\n  draft --&gt; cancelled: cancel\n  submitted --&gt; cancelled: cancel\n  processing --&gt; cancelled: cancel\n\n\nAnd here’s what that looks like when you’re checking the transitions with actual API calls:\n\nworkflows:\n  - workflowId: order-lifecycle\n    summary: Complete lifecycle of an order from draft to delivered\n    \n    steps:\n      - stepId: createDraft\n        description: Create order in draft state\n        operationId: createOrder\n        successCriteria:\n          - condition: $response.body#/state == 'draft'\n      \n      - stepId: submitOrder\n        description: Transition from draft to submitted\n        operationId: submitOrder\n        parameters:\n          - name: orderId\n            value: $steps.createDraft.outputs.orderId\n        successCriteria:\n          - condition: $response.body#/state == 'submitted'\n      \n      - stepId: processPayment\n        description: Process payment, moving to processing state\n        operationId: processPayment\n        successCriteria:\n          - condition: $response.body#/state == 'processing'\n          - condition: $response.body#/paymentStatus == 'paid'\n      \n      - stepId: ship\n        description: Ship the order\n        operationId: shipOrder\n        successCriteria:\n          - condition: $response.body#/state == 'shipped'\n        \n      - stepId: confirmDelivery\n        description: Final state transition to delivered\n        operationId: confirmDelivery\n        successCriteria:\n          - condition: $response.body#/state == 'delivered'\n\n\nInstead of being locked away in some cloud testing environment or hidden QA repository for only a select few to see, the criteria for success and failure become easily visible and knowable to everyone.\n\nNew team members can read the workflow and understand the valid transitions. QA can execute the same workflow as an end-to-end test. When a rule is changed (say you add a “refunded” state), you update one workflow and let tooling catch the places that no longer match. This can be done along with the code changes in a single pull request, making reviews and validation much simpler.\n\nYou can also handle real-world edge cases without turning your docs into a wall of prose. For example: “cancel is allowed in three states, but if it’s already shipped you need to start a refund flow instead”.\n\n- stepId: cancelOrder\n  description: Cancel order - allowed from draft, submitted, or processing states\n  operationId: cancelOrder\n  successCriteria:\n    - condition: $statusCode == 200\n    - condition: $response.body#/state == 'cancelled'\n  \n  # This step can be reached from multiple prior states\n  dependsOn:\n    - createDraft\n    - submitOrder  \n    - processPayment\n  \n  # Only execute if certain conditions are met\n  onFailure:\n    - name: orderAlreadyShipped\n      type: goto\n      stepId: refundProcess\n      criteria:\n        - condition: $response.body#/reason == 'ORDER_SHIPPED'\n\n\nDesign, governance, and operations\n\nOnce you’ve got a handful of core workflows, they naturally become a contract for how the API should be used. That helps during design reviews. Can a customer actually complete checkout with this API? Did a recent change break a critical journey?\n\nOperationally, workflows are useful as smoke tests and as synthetic monitoring. This is different from a classic “is the service up?” health check: you’re not trying to prove the database is reachable, you’re trying to prove the product still works.\n\nIf the “signup → create resource → view resource” journey is broken, the API might still be returning 200s all day long, but users are stuck. Arazzo lets you encode that journey once and run it on a schedule (either on a testing environment, sandbox, or production if you’re careful).\n\nHere’s a compact example of a synthetic canary workflow you might run in CI or periodically in a monitoring job:\n\nworkflows:\n  - workflowId: canary-happy-path\n    summary: Validate a critical user journey end-to-end\n    steps:\n      - stepId: authenticate\n        operationId: getToken\n      - stepId: createResource\n        operationId: createItem\n      - stepId: readBack\n        operationId: getItem\n      - stepId: cleanup\n        operationId: deleteItem\n\n\nEven if you never generate a fancy visualization as documentation, the day-to-day payoff is simple: onboarding is faster when people can run the workflow and watch it work; integration is smoother when the happy path is explicit; and breakages are easier to spot because failures show up as a specific step, with a specific condition that didn’t match.\n\nCross-API orchestration\n\nWhile OpenAPI describes a single API, Arazzo can orchestrate across multiple APIs by referencing multiple source descriptions. This is especially useful for documenting and testing business processes that span different APIs or services.\n\nsourceDescriptions:\n  - name: paymentApi\n    url: ./payment-api.yaml\n  - name: inventoryApi\n    url: ./inventory-api.yaml\n  - name: shippingApi\n    url: ./shipping-api.yaml\n\nworkflows:\n  - workflowId: complete-order\n    steps:\n      - stepId: reserveInventory\n        operationId: $sourceDescriptions.inventoryApi.reserve\n      \n      - stepId: processPayment\n        operationId: $sourceDescriptions.paymentApi.charge\n        \n      - stepId: scheduleShipping\n        operationId: $sourceDescriptions.shippingApi.schedule\n        onFailure:\n          - name: releaseInventory\n            type: goto\n            stepId: rollbackInventory\n\n\nNotice the difference in operationId syntax. Instead of referencing just the operationId (e.g.; reserve), a longer runtime expression is used which references the API in the context of its source: $sourceDescriptions.inventoryApi.reserve.\n\nThis is where Arazzo starts to feel more like CI/CD workflows, but for business journeys. Stitching together operations that live in different services, passing data between them, and defining what “success” means for the whole sequence in a single source of truth.\n\nBusiness value\n\nBeyond making sure an API ecosystem is actually functioning properly (with fewer partial sources of truth to disagree with each other) most of the business value is second-order effects.\n\n\n  Properly documented flows lead to fewer support requests from confused client developers struggling to integrate.\n  Reduced chances of AI agents flailing around hallucinating incorrect API usage.\n  More confidence when you ship changes because you can run the same workflows as regression tests.\n  All of that saves time and money."
        },
        {
          "id": "arazzo-v1.0-understanding-structure-basic-structure",
          "title": "Basic structure",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/arazzo/v1.0/understanding-structure/basic-structure/",
          "content": "The Arazzo document\n  1. Arazzo version\n  2. Info object\n  3. Source descriptions\n  4. Workflows\n  5. Components\n  Putting it all together\n  Next steps\n\n\nThe Arazzo Specification defines how exactly an Arazzo document should be laid out, and understanding this structure is essential for creating effective HTTP workflow documentation.\n\nThe Arazzo document\n\nAn Arazzo document is written as YAML or JSON. In a simple world you would typically name it arazzo.yaml or arazzo.json, but you can use any name you prefer.\n\nLet’s go through the main sections in an Arazzo document:\n\n\n  Arazzo version\n  Info object\n  Source descriptions\n  Workflows\n  Components\n\n\nLet’s examine each of these in detail.\n\n1. Arazzo version\n\nThe root of every Arazzo document must specify the version of the Arazzo Specification being used:\n\narazzo: 1.0.1\n\n\nThe arazzo field is required, and tells tooling which version of the specification to use when parsing the document. The latest version at time of writing is 1.0.1, but there is no semantic difference between 1.0.0 and 1.0.1. It is just good practice to use the latest patch version.\n\n2. Info object\n\nThe info object provides metadata about the workflow document:\n\ninfo:\n  title: Train Travel Workflows\n  version: 1.0.0\n  summary: Common workflows for the train ticket API\n  description: |\n    Workflows for working with the Train Travel API, covering\n    searching for trips, making bookings, and managing tickets.\n\n\nRequired fields:\n\n\n  title - A human-readable name for your workflow document\n  version - The version of this workflow document (independent of API version or Arazzo spec version)\n\n\nOptional fields:\n\n\n  summary - A short summary of what these workflows accomplish\n  description - A longer description, supports CommonMark (Markdown)\n\n\nThe info object is similar to the OpenAPI info object, but describes the workflows rather than an API.\n\n3. Source descriptions\n\nThe sourceDescriptions array references the API definitions that workflows will use, which will often just be a single openapi document but could be multiple APIs or even other Arazzo documents.\n\nsourceDescriptions:\n  - name: trainApi\n    url: https://api.example.com/openapi.yaml\n    type: openapi\n  \n  - name: paymentApi\n    url: ./payment-api.json\n    type: openapi\n\n\nEach source description has the following fields:\n\n\n  name - An identifier used to reference APIs when multiple sources are used in a workflow. Some people prefer camelCase (e.g.; trainApi), because these will be used in runtime expressions later on.\n  url - Location of the API description, could be a URL or relative file path.\n  type - The type of API description (openapi to reference an API, or arazzo to reference another Arazzo document).\n\n\nLearn more about source descriptions.\n\n4. Workflows\n\nThe workflows array is where the magic really happens. Each workflow describes a sequence of steps to accomplish a given task:\n\nworkflows:\n  - workflowId: book-train-ticket\n    summary: Complete workflow for booking a train ticket\n    description: Searches for trips, selects one, and creates a booking\n    \n    inputs:\n      type: object\n      properties:\n        origin:\n          type: string\n        destination:\n          type: string\n        passengers:\n          type: array\n    \n    steps:\n      - stepId: search\n        description: Search for available trips\n        operationId: $sourceDescriptions.trainApi.searchTrips\n        parameters:\n          - name: origin\n            in: query\n            value: $inputs.origin\n          - name: destination\n            in: query\n            value: $inputs.destination\n        \n        successCriteria:\n          - condition: $statusCode == 200\n        \n        outputs:\n          tripId: $response.body#/trips/0/id\n      \n      - stepId: createBooking\n        description: Create booking for the selected trip\n        operationId: $sourceDescriptions.trainApi.createBooking\n        requestBody:\n          contentType: application/json\n          payload:\n            tripId: $steps.search.outputs.tripId\n            passengers: $inputs.passengers\n        \n        successCriteria:\n          - condition: $statusCode == 201\n        \n        outputs:\n          bookingId: $response.body#/id\n\n\nEach workflow contains:\n\nWorkflow metadata:\n\n\n  workflowId (required) - Unique identifier for this workflow.\n  summary - A shorter description (used as a title).\n  description - Longer form description, with CommonMark (Markdown) support.\n  inputs - JSON Schema defining what inputs the workflow accepts.\n  outputs - What outputs the workflow produces.\n\n\nSteps array:\n\n\n  stepId (required) - Unique identifier for this step within the workflow.\n  operationId - Reference to an API operation to call.\n  parameters - Parameters to pass to the operation.\n  requestBody - Request body to send.\n  successCriteria - Conditions that define success.\n  onSuccess / onFailure - Actions to take based on results.\n  outputs - Values to extract from the response.\n\n\nLearn more about Workflows.\n\n5. Components\n\nIn order to cut down on repetition - which is tedious and to keep up to date, and a vector for making mistakes - commonly used chunks of Arazzo can be defined in the components object.\n\nComponents can define:\n\n\n  inputs - Reusable input schemas.\n  parameters - Reusable parameters.\n  successActions - Reusable success actions.\n  failureActions - Reusable failure actions.\n\n\ncomponents:\n  inputs:\n    pagination:\n      type: object\n      properties:\n        page:\n          type: integer\n          default: 1\n        pageSize:\n          type: integer\n          default: 20\n  \n  parameters:\n    authHeader:\n      name: Authorization\n      in: header\n      value: 'Bearer {$inputs.token}'\n  \n  successActions:\n    logSuccess:\n      type: end\n      name: logSuccess\n  \n  failureActions:\n    logFailure:\n      type: end\n      name: logFailure\n\n\nLearn more about components and referencing.\n\nPutting it all together\n\nTo see how this structure all works together, here’s a complete (but very minimal) Arazzo document showing the key bits, including a couple of reusable components:\n\narazzo: 1.0.1\n\ninfo:\n  title: Train Travel Workflows\n  version: 1.0.0\n  summary: Common workflows for the train ticket API\n\nsourceDescriptions:\n  - name: trainApi\n    url: https://api.example.com/openapi.yaml\n    type: openapi\n\ncomponents:\n  inputs:\n    bookTrainTicketInput:\n      type: object\n      required: [origin, destination, passengers, token]\n      properties:\n        origin:\n          type: string\n        destination:\n          type: string\n        passengers:\n          type: array\n        token:\n          type: string\n\nworkflows:\n  - workflowId: book-train-ticket\n    summary: Complete workflow for booking a train ticket\n    \n    inputs:\n      $ref: '#/components/inputs/bookTrainTicketInput'\n\n    steps:\n      - stepId: search\n        description: Search for available trips\n        operationId: $sourceDescriptions.trainApi.searchTrips\n        parameters:\n          - name: origin\n            in: query\n            value: $inputs.origin\n          - name: destination\n            in: query\n            value: $inputs.destination\n        successCriteria:\n          - condition: $statusCode == 200\n\n        outputs:\n          tripId: $response.body#/trips/0/id\n\n      - stepId: createBooking\n        description: Create booking for the selected trip\n        operationId: $sourceDescriptions.trainApi.createBooking\n        requestBody:\n          contentType: application/json\n          payload:\n            tripId: $steps.search.outputs.tripId\n            passengers: $inputs.passengers\n        successCriteria:\n          - condition: $statusCode == 201\n\n        outputs:\n          bookingId: $response.body#/id\n\n\nNext steps\n\nNow that you understand the basic structure, if you’d like to learn more about any of the sections in particular you can take a look at these guides.\n\n\n  Defining sources - How to reference API definitions\n  Workflows - Creating workflow sequences\n  Steps, inputs, and outputs - Working with data flow\n  Success and failure - Handling outcomes\n  Components &amp; references - Reusing definitions"
        },
        {
          "id": "arazzo-v1.0-understanding-structure-defining-sources",
          "title": "Defining sources",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/arazzo/v1.0/understanding-structure/defining-sources/",
          "content": "The sourceDescriptions array\n  Source description fields    \n      name (required)\n      url (required)\n      type (required)\n    \n  \n  Referencing operations from sources    \n      OpenAPI operation IDs\n    \n  \n  Multiple sources\n  Composing Arazzo documents\n\n\nSource descriptions are how Arazzo gets a leg up on understanding the APIs being worked with. To avoid repeating all the HTTP-level bits like endpoints, status codes, schema, etc. Arazzo just defines source description, then references operations by their operationId. Most source descriptions are OpenAPI documents. However, you can also reference workflows defined in other Arazzo documents, and support for AsyncAPI documents is coming soon.\n\nThe sourceDescriptions array\n\nSource descriptions are described in the sourceDescriptions array at the root level, and there needs to be at least one:\n\narazzo: 1.0.1\ninfo:\n  title: My Workflows\n  version: 1.0.0\n\nsourceDescriptions:\n  - name: trainApi\n    url: openapi.yaml\n    type: openapi\n  \n  - name: paymentGateway\n    url: https://api.example.com/payment-service.json\n    type: openapi\n\nworkflows:\n  # ... workflows reference these sources\n\n\nThis array has the two APIs, one local OpenAPI document written in YAML and a payment service where the OpenAPI is hosted remotely, available as a JSON URl. That’ll all work fine, and makes it easier to work across context boundaries where different teams might manage their own APIs and OpenAPI.\n\nSource description fields\n\nLet’s have a bit of a closer look at the fields that make up a source description. Each source description object has three required fields:\n\n- name: myApi\n  url: ./path/to/api-description.yaml\n  type: openapi\n\n\nname (required)\n\nTo avoid having to call up that API description by its full path or URL every time, or refer to it by name “Barry’s Badly Named API v231.3-final” we give it a short name that can be reused more easily. These short names are used as variables when referencing operations from that source.\n\nsourceDescriptions:\n  - name: bookingService\n    url: ./booking-api.yaml\n    type: openapi\n\nworkflows:\n  - workflowId: book-ticket\n    steps:\n      - stepId: create\n        operationId: $sourceDescriptions.bookingService.createBooking\n\n\nWhen there is only one source defined you can skip $sourceDescriptions.bookingService. and just use createBooking but when a second source is added the full reference is required to avoid ambiguity.\n\nurl (required)\n\nThe location of the API description document. This can be a relative path:\n\nurl: ./openapi.yaml\nurl: ../apis/booking.yaml\nurl: ./specs/v2/openapi.json\n\n\nOr it can be an absolute URL:\n\nurl: https://api.example.com/openapi.yaml\nurl: https://raw.githubusercontent.com/org/repo/main/openapi.json\n\n\ntype (required)\n\nThe type of API description format. Current valid values:\n\n\n  openapi - Supporting OpenAPI v3.x or v2.0 (formerly known as Swagger)\n  arazzo - Reference other Arazzo documents.\n\n\nThis can be used to mix and match different API description formats in the same Arazzo document:\n\nsourceDescriptions:\n  - name: restApi\n    url: ./openapi.yaml\n    type: openapi\n  \n  - name: commonWorkflows\n    url: ./shared-workflows.yaml\n    type: arazzo\n\n\nReferencing operations from sources\n\nOnce you’ve defined a source, you reference operations using the operationId with a runtime expression in the following format: $sourceDescriptions.{name}.{operationId}.\n\nsourceDescriptions:\n  - name: trainApi\n    url: ./train-api.yaml\n    type: openapi\n\nworkflows:\n  - workflowId: search-trains\n    steps:\n      - stepId: search\n        operationId: $sourceDescriptions.trainApi.searchTrips\n\n\nThe operation must exist in the referenced API definition with a matching operationId.\n\nOpenAPI operation IDs\n\nIn your OpenAPI document:\n\n# train-api.yaml\npaths:\n  /trips:\n    get:\n      operationId: searchTrips  # This is what Arazzo references\n      summary: Search for train trips\n      # ...\n\n\nThe operationId in OpenAPI should be unique across the entire API description. You’ll also need to be cautious when renaming operationId or sunsetting old endpoints as it could break some workflows.\n\nMultiple sources\n\nSimple workflows might only need one API but when working on cross-API orchestration (e.g., microservices, third-party integrations) or writing end-to-end tests that cross multiple APIs/services, you’ll likely need to define multiple sources:\n\nsourceDescriptions:\n  - name: inventoryApi\n    url: https://inventory.example.com/openapi.yaml\n    type: openapi\n  \n  - name: paymentApi\n    url: https://payments.example.com/openapi.yaml\n    type: openapi\n  \n  - name: shippingApi\n    url: https://shipping.example.com/openapi.yaml\n    type: openapi\n\nworkflows:\n  - workflowId: complete-order\n    summary: Orchestrate order across multiple services\n    steps:\n      - stepId: checkInventory\n        operationId: $sourceDescriptions.inventoryApi.checkStock\n        \n      - stepId: reserveItems\n        operationId: $sourceDescriptions.inventoryApi.reserveStock\n        \n      - stepId: processPayment\n        operationId: $sourceDescriptions.paymentApi.createCharge\n        \n      - stepId: scheduleShipping\n        operationId: $sourceDescriptions.shippingApi.createShipment\n        \n      - stepId: confirmOrder\n        operationId: $sourceDescriptions.inventoryApi.confirmReservation\n\n\nThis is powerful for orchestrating across multiple microservices, combining your API with external third-party integrations, and using multiple providers together in multi-vendor workflows.\n\nComposing Arazzo documents\n\nArazzo documents can reference other Arazzo documents to compose workflows, enabling you to share common workflows across different teams or projects, or build complex workflows from simpler, reusable components.\n\n# common-workflows.yaml\narazzo: 1.0.0\ninfo:\n  title: Common Workflows\n  version: 1.0.0\n\nsourceDescriptions:\n  - name: authApi\n    url: ./auth-api.yaml\n    type: openapi\n\nworkflows:\n  - workflowId: authenticate\n    summary: Standard authentication workflow\n    steps:\n      - stepId: getToken\n        operationId: $sourceDescriptions.authApi.createToken\n        outputs:\n          token: $response.body#/access_token\n\n\n# booking-workflows.yaml\narazzo: 1.0.0\ninfo:\n  title: Booking Workflows\n  version: 1.0.0\n\nsourceDescriptions:\n  - name: bookingApi\n    url: ./booking-api.yaml\n    type: openapi\n  \n  - name: commonFlows\n    url: ./common-workflows.yaml\n    type: arazzo  # Reference another Arazzo document\n\nworkflows:\n  - workflowId: authenticated-booking\n    steps:\n      # Use a workflow from another Arazzo document\n      - stepId: auth\n        workflowId: $sourceDescriptions.commonFlows.authenticate\n      \n      - stepId: book\n        operationId: $sourceDescriptions.bookingApi.createBooking\n        parameters:\n          - name: Authorization\n            in: header\n            value: 'Bearer {$steps.auth.outputs.token}'\n\n\nThis enables workflow reuse by sharing common workflows across documents, modularity by organizing workflows by domain or team, and composition by building complex workflows from simpler ones."
        },
        {
          "id": "arazzo-v1.0-understanding-structure-success-and-failure",
          "title": "Success and failure in API workflows",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/arazzo/v1.0/understanding-structure/success-and-failure/",
          "content": "Criteria object\n  Success and failure actions    \n      Action types\n      Invoking workflows on actions\n    \n  \n  Examples    \n      Branching on search results\n      Branching on booking status\n      Multi-passenger validation\n    \n  \n  Best practices    \n      Validate business rules, not just HTTP\n      Reusable actions with components\n    \n  \n\n\nSome API workflow tools just check HTTP status codes for a 200 OK or a 500 server error and decide based on that! Real-world APIs are far more involved that. A 200 response might contain an empty result set. A 404 might be totally expected in a given scenario. Arazzo gives you fine-grained control over what “success” and “failure” actually mean for the use-case and work flow by allowing criteria to be defined for each step.\n\n- stepId: search\n  operationId: $sourceDescriptions.api.searchTrips\n  successCriteria:\n    - condition: $statusCode == 200\n    - type: jsonpath\n      context: $response.body\n      condition: $.trips[0] != null\n  failureCriteria:\n    - condition: $statusCode == 404\n\n\nSometimes HTTP says might say “success”, but the business logic says “nope!” so it’s helpful to not rely entirely on status checks. Imagine a search API that returns 200 OK, but with zero results. Or an inventory check that returns 200 with {\"available\": false}. These need explicit criteria because it could be a success or a failure depending on the context of that workflow.\n\nHere are a few things we might want to check:\n\n\n  The response contains the data you expected.\n  Specific fields have the right values.\n  Arrays have at least one item (or exactly N items).\n  Headers indicate proper caching or rate limiting.\n  The response structure matches your schema.\n\n\nBoth the success and failure criteria are arrays of checks that work in in the same way, using the same criteria objects.\n\nCriteria object\n\nEvery one of these conditions must pass for the step to be considered successful, making it an AND operation, not an OR.\n\nsteps:\n- stepId: exampleStep\n  operationId: $sourceDescriptions.api.exampleOperation\n\n  successCriteria:\n    - condition: $statusCode == 200\n    - condition: $response.body#/results != null\n\n  failureCriteria:\n    - context: $response.body\n      condition: $.errors[0] != null\n      type: jsonpath\n\n\ncondition (required) - A boolean expression that must evaluate to true.\n\nsuccessCriteria:\n  - condition: $statusCode == 200\n  - condition: $response.body#/available == true\n\n\ntype (optional) - Type of criterion (defaults to simple):\n\n\n  simple - Basic condition evaluation.\n  regex - Regular expression matching.\n  jsonpath - JSONPath query.\n\n\ncontext (required when type == regex or jsonpath) - Which bit of the data are we evaluating. This could be $response.body, $response.headers, or any other valid runtime expressions.\n\nsuccessCriteria:\n  - condition: $response.body#/status == 'confirmed'\n\n\nSuccess and failure actions\n\nOnce criteria determine whether a step has succeeded or failed, what happens next? By default, the workflow continues to the next sequential step. But we can also define onSuccess and onFailure actions to branch the workflow based on outcomes.\n\nActions can be defined inline or referenced from reusable components to maintain consistency across workflows.\n\nAction types\n\nBoth onSuccess and onFailure use the same action types. Each action has a name, a type (what to do), optional criteria which is a list of assertions to see if this action should be executed, and type-specific fields.\n\ntype: end - Stops the workflow immediately.\n\nonFailure:\n  - name: criticalError\n    type: end\n    criteria:\n      - condition: $statusCode &gt;= 500\n\n\ntype: goto - Jumps to another step (perfect for error handlers or alternative paths).\n\nonFailure:\n  - name: tryAlternative\n    type: goto\n    stepId: alternativeBookingMethod\n    criteria:\n      - condition: $statusCode == 429  # Too Many Requests\n\nonSuccess:\n  - name: continueToPayment\n    type: goto\n    stepId: processPayment\n    criteria:\n      - condition: $response.body#/requiresPayment == true\n\n\ntype: retry - Tries the same step again (with optional delays and limits).\n\nonFailure:\n  - name: retryOnTimeout\n    type: retry\n    retryAfter: 5  # seconds\n    retryLimit: 3  # attempts\n    criteria:\n      - condition: $statusCode == 408  # Request Timeout\n\n\nInvoking workflows on actions\n\nSometimes a single step can’t handle the recovery or next phase. The workflowId field lets actions invoke other workflows for complex scenarios:\n\nonFailure:\n  # Expired token - refresh and retry\n  - name: refreshExpiredToken\n    type: retry\n    workflowId: refreshTokenWorkflow\n    retryAfter: 1\n    retryLimit: 3\n    criteria:\n      - condition: $statusCode == 401\n      - condition: $response.body#/errorCode == 'TOKEN_EXPIRED'\n  \n  # Primary API down - switch to backup\n  - name: useBackupApi\n    type: goto\n    workflowId: backupApiSearchWorkflow\n    stepId: searchWithBackup\n    criteria:\n      - condition: $statusCode &gt;= 500\n\n\nThis is particularly useful for:\n\n\n  Token refresh flows - When authentication expires, invoke a workflow to get new credentials and retry.\n  Fallback API chains - Try alternative APIs or services when primary ones fail.\n  Data synchronization - Trigger reconciliation workflows when data inconsistencies are detected.\n  Escalation procedures - Invoke notification and logging workflows for critical errors.\n\n\nThe workflow will run completely when it’s invoked, then returns to the current step to continue processing based on the result.\n\nExamples\n\nLet’s rattle through a few more complete scenarios to see how it all fits together.\n\nBranching on search results\n\nA more advanced workflow has been created for the Train Travel API which allows folks to search for train trips, and based on various critera it will either look for better trips or go ahead and book.\n\nworkflows:\n  - workflowId: searchAndBookTrips\n    summary: Search for train trips and handle different results\n    inputs:\n      type: object\n      properties:\n        origin:\n          type: string\n        destination:\n          type: string\n        departureDate:\n          type: string\n        maxPrice:\n          type: number\n    \n    steps:\n      - stepId: searchTrips\n        operationId: $sourceDescriptions.trainApi.searchTrips\n        parameters:\n          - name: origin\n            in: query\n            value: $inputs.origin\n          - name: destination\n            in: query\n            value: $inputs.destination\n          - name: date\n            in: query\n            value: $inputs.departureDate\n        \n        successCriteria:\n          - condition: $statusCode == 200\n          - type: jsonpath\n            context: $response.body\n            condition: $.trips[0] != null\n\n        onSuccess:\n          # Found affordable trips - proceed to booking\n          - name: foundAffordableTrips\n            type: goto\n            stepId: selectTrip\n            criteria:\n              - type: jsonpath\n                context: $response.body\n                condition: $.trips[?(@.price &lt;= $inputs.maxPrice)][0] != null\n          \n          # Only expensive trips - offer alternatives\n          - name: onlyExpensiveTrips\n            type: goto\n            stepId: suggestAlternativeDates\n            criteria:\n              - type: jsonpath\n                context: $response.body\n                condition: $.trips[?(@.price &lt;= $inputs.maxPrice)][0] == null\n        \n        onFailure:\n          # No trips available - try different dates\n          - name: noTripsAvailable\n            type: goto\n            stepId: searchAlternativeDates\n            criteria:\n              - type: jsonpath\n                context: $response.body\n                condition: $.trips[0] == null\n          \n          # API error - retry\n          - name: apiError\n            type: retry\n            retryAfter: 5\n            retryLimit: 3\n            criteria:\n              - condition: $statusCode &gt;= 500\n      \n      - stepId: selectTrip\n        # ... trip selection logic\n      \n      - stepId: suggestAlternativeDates\n        # ... alternative date suggestions\n      \n      - stepId: searchAlternativeDates\n        # ... search with different dates\n\n\nThis workflow branches based on the search results:\n\n\n  Affordable trips exist - Go to trip selection.\n  Only expensive trips - Suggest alternative dates.\n  No trips at all - Also suggest alternative dates.\n  API failure - Retry the search.\n\n\nBranching on booking status\n\nOnce a trip is selected, creating the booking might succeed in different ways:\n\nworkflows:\n  - workflowId: createTripBooking\n    summary: Create booking with different confirmation flows\n    inputs:\n      type: object\n      properties:\n        passengers:\n          type: array\n    \n    steps:\n      - stepId: createBooking\n        operationId: $sourceDescriptions.trainApi.createBooking\n        requestBody:\n          payload:\n            tripId: $steps.selectTrip.outputs.selectedTripId\n            passengers: $inputs.passengers\n        \n        successCriteria:\n          - condition: $statusCode == 201\n          - condition: $response.body#/id != null\n        \n        onSuccess:\n          # Booking confirmed immediately - skip to payment\n          - name: instantConfirmation\n            type: goto\n            stepId: processPayment\n            criteria:\n              - condition: $response.body#/status == 'confirmed'\n          \n          # Pending confirmation - wait for availability check\n          - name: pendingConfirmation\n            type: goto\n            stepId: pollBookingStatus\n            criteria:\n              - condition: $response.body#/status == 'pending'\n          \n          # Free trip (promotional) - skip payment\n          - name: freeTrip\n            type: goto\n            stepId: sendConfirmationEmail\n            criteria:\n              - condition: $response.body#/totalPrice == 0\n        \n        onFailure:\n          # Seats sold out - offer alternative trips\n          - name: seatsUnavailable\n            type: goto\n            stepId: findAlternativeTrips\n            criteria:\n              - condition: $statusCode == 409\n              - condition: $response.body#/errorCode == 'SEATS_UNAVAILABLE'\n          \n          # Invalid passenger data - return to form\n          - name: invalidPassengerData\n            type: goto\n            stepId: notifyValidationError\n            criteria:\n              - condition: $statusCode == 400\n              - condition: $response.body#/errorCode == 'INVALID_PASSENGER_DATA'\n      \n      - stepId: processPayment\n        # ... payment processing\n      \n      - stepId: pollBookingStatus\n        # ... poll for booking confirmation\n      \n      - stepId: sendConfirmationEmail\n        # ... send confirmation\n      \n      - stepId: findAlternativeTrips\n        # ... find other available trips\n      \n      - stepId: notifyValidationError\n        # ... notify about validation issues\n\n\nMulti-passenger validation\n\nWhen handling multiple passengers, validate all requirements before proceeding:\n\nworkflows:\n  - workflowId: validateTripPassengers\n    summary: Validate passenger data with different requirements\n    inputs:\n      type: object\n      properties:\n        passengers:\n          type: array\n    \n    steps:\n      - stepId: validatePassengers\n        operationId: $sourceDescriptions.trainApi.validatePassengerData\n        requestBody:\n          payload:\n            passengers: $inputs.passengers\n            tripId: $steps.selectTrip.outputs.selectedTripId\n        \n        successCriteria:\n          - condition: $statusCode == 200\n          - type: jsonpath\n            context: $response.body\n            condition: $.passengers[?(@.valid == false)][0] == null\n        \n        onSuccess:\n          # All passengers valid - proceed\n          - name: allPassengersValid\n            type: goto\n            stepId: createBooking\n        \n        onFailure:\n          # Child without guardian - request guardian details\n          - name: childWithoutGuardian\n            type: goto\n            stepId: requestGuardianInfo\n            criteria:\n              - type: jsonpath\n                context: $response.body\n                condition: $.passengers[?(@.age &lt; 16 &amp;&amp; @.guardianId == null)][0] != null\n          \n          # Senior discount requires ID verification\n          - name: seniorRequiresVerification\n            type: goto\n            stepId: verifySeniorDiscount\n            criteria:\n              - type: jsonpath\n                context: $response.body\n                condition: $.passengers[?(@.age &gt;= 65 &amp;&amp; @.idVerified == false)][0] != null\n          \n          # Invalid passport for international trip\n          - name: invalidPassport\n            type: goto\n            stepId: requestValidPassport\n            criteria:\n              - condition: $response.body#/tripType == 'international'\n              - type: jsonpath\n                context: $response.body\n                condition: $.passengers[?(@.passportValid == false)][0] != null\n        \n      - stepId: createBooking\n        # ... proceed with booking\n      \n      - stepId: requestGuardianInfo\n        # ... request guardian details for minors\n      \n      - stepId: verifySeniorDiscount\n        # ... verify senior citizen ID\n      \n      - stepId: requestValidPassport\n        # ... request valid passport for international travel\n\n\nBest practices\n\nValidate business rules, not just HTTP\n\nThe goal is to ensure the workflow meets business needs, not just technical success at the transportation level. Get business logic written down, and if the logic changes that’s ok, the workflow can be updated to match.\n\n# Good - checks business requirements\nsuccessCriteria:\n  - condition: $statusCode == 200\n  - condition: $response.body#/status == 'confirmed'\n  - condition: $response.body#/seats != null\n  - condition: $response.body#/totalPrice &lt;= $inputs.maxBudget\n\n# Insufficient - only checks HTTP\nsuccessCriteria:\n  - condition: $statusCode == 200\n\n\nReusable actions with components\n\nFor actions used across multiple steps or workflows, define them in the components section:\n\ncomponents:\n  failureActions:\n    # Reusable token refresh\n    refreshToken:\n      name: refreshExpiredToken\n      type: retry\n      workflowId: refreshTokenWorkflow\n      retryAfter: 1\n      retryLimit: 3\n      criteria:\n        - condition: $statusCode == 401\n    \n    # Reusable backup API fallback\n    useBackupSystem:\n      name: switchToBackupApi\n      type: goto\n      workflowId: backupSystemWorkflow\n      stepId: retryWithBackup\n      criteria:\n        - condition: $statusCode &gt;= 500\n        - condition: $response.headers.retry-after == null\n\n\nThen reference them in your steps:\n\n- stepId: searchTrips\n  operationId: $sourceDescriptions.trainApi.searchTrips\n  onFailure:\n    - reference: $components.failureActions.refreshToken\n    - reference: $components.failureActions.useBackupSystem\n\n\nThis keeps your workflows clean and ensures consistent error handling across your entire API workflow description."
        },
        {
          "id": "arazzo-v1.0-understanding-structure-steps-inputs-outputs",
          "title": "Steps, inputs, and outputs",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/arazzo/v1.0/understanding-structure/steps-inputs-outputs/",
          "content": "What is a step?    \n      Required fields\n      Optional fields\n    \n  \n  Step execution order    \n      Controlling flow with actions\n    \n  \n  Working with inputs    \n      1. Workflow inputs\n      2. Previous step outputs\n      3. Global parameters\n    \n  \n  Parameters    \n      Query parameters\n      Path parameters\n      Header parameters\n      Cookie parameters\n    \n  \n  Request bodies    \n      Simple request body\n      Complex request body\n      Using previous step data\n      Different content types\n    \n  \n  Outputs    \n      Basic outputs\n      JSON pointer notation\n      Headers and status\n    \n  \n  Using outputs in subsequent steps\n  Best practices    \n      Descriptive step IDs\n      Extract useful outputs\n      Clear descriptions\n    \n  \n  Wrapping up\n\n\nIf workflows are the recipes, then steps are the individual cooking instructions. Each step does one thing: call an API endpoint, run another workflow, or make a decision. Steps get their data from inputs and output from other steps, do their work, then save interesting bits of the response for steps that come after.\n\nWhat is a step?\n\nA step is a single action in your workflow. Most often that means calling an API endpoint, but steps can also invoke other workflows or handle conditional branching.\n\nHere’s an example step that operates within a workflow to search for train trips:\n\nsteps:\n  - stepId: searchTrips\n    description: Search for available train trips\n    operationId: $sourceDescriptions.trainApi.searchTrips\n    parameters:\n      - name: origin\n        in: query\n        value: $inputs.origin\n    successCriteria:\n      - condition: $statusCode == 200\n    outputs:\n      firstTripId: $response.body#/trips/0/id\n\n\nRequired fields\n\nstepId - The unique name for this step within the workflow.\n\nsteps:\n  - stepId: search\n  - stepId: createBooking\n  - stepId: processPayment\n\n\nStick to descriptive names in camelCase or kebab-case. This ID is how other steps reference this step’s outputs, and how control flow (like goto) targets specific steps.\n\nEvery step needs to specify what it’s going to do, and that will be a single operation or a whole other workflow. Operations can be picked with the operationId if the OpenAPI has defined operationIds (best practice, and most linters will pester you to do this), or operationPath if operationIds are missing. Kicking off a whole workflow can be done with workflowId.\n\noperationId - References an operation from your source APIs.\n\n- stepId: getBooking\n  operationId: $sourceDescriptions.trainApi.getBookingById\n\n\noperationPath - Reference by HTTP method and path. Requires a bit more implementation detail and will break when a path or parameter changes.\n\n- stepId: getBooking\n  operationPath: /bookings/{bookingId}\n  method: get  # Will also need to specify method\n\n\nworkflowId - Execute another workflow.\n\n- stepId: cancelBooking\n  workflowId: $sourceDescriptions.trainApi.cancelBookingWorkflow\n\n\nOptional fields\n\ndescription - Explain what the step does in a way that is useful for human-readable documentation.\n\n- stepId: search\n  description: Search for train trips between two stations on a specific date\n  operationId: $sourceDescriptions.trainApi.searchTrips\n\n\nparameters - Override or add parameters to the operation.\n\n- stepId: search\n  operationId: $sourceDescriptions.trainApi.searchTrips\n  parameters:\n    - name: origin\n      in: query\n      value: $inputs.origin\n    - name: destination\n      in: query\n      value: $inputs.destination\n\n\nrequestBody - Define the request body for POST/PUT/PATCH/QUERY operations.\n\n- stepId: createBooking\n  operationId: $sourceDescriptions.trainApi.createBooking\n  requestBody:\n    contentType: application/json\n    payload:\n      tripId: $steps.search.outputs.tripId\n      passengers: $inputs.passengers\n\n\nsuccessCriteria - Define what counts as a successful step execution.\n\n- stepId: search\n  operationId: $sourceDescriptions.trainApi.searchTrips\n  successCriteria:\n    - condition: $statusCode == 200\n    - context: $response.body\n      condition: $[?count(@.trips) &gt; 0]\n      type: jsonpath\n\n\nonSuccess / onFailure - Actions to take based on outcome.\n\n- stepId: checkAvailability\n  operationId: $sourceDescriptions.api.checkStock\n  successCriteria:\n    - condition: $response.body#/available == true\n  onFailure:\n    - name: soldOut\n      type: end\n\n\noutputs - Extract data from the response.\n\n- stepId: createBooking\n  operationId: $sourceDescriptions.api.createBooking\n  outputs:\n    bookingId: $response.body#/id\n    status: $response.body#/status\n    totalPrice: $response.body#/pricing/total\n\n\nStep execution order\n\nBy default, steps run in order from top to bottom. Step 1 finishes, then step 2 runs, then step 3, and so on. Simple and predictable:\n\nsteps:\n  - stepId: step1  # Executes first\n    # ...\n  \n  - stepId: step2  # Executes second (after step1 completes)\n    # ...\n  \n  - stepId: step3  # Executes third (after step2 completes)\n    # ...\n\n\nControlling flow with actions\n\nBut sometimes you need to jump around. The onSuccess and onFailure fields let you break out of that linear flow:\n\nsteps:\n  - stepId: checkInventory\n    operationId: $sourceDescriptions.api.checkStock\n    onSuccess:\n      - name: proceed\n        type: goto\n        stepId: createBooking\n    onFailure:\n      - name: unavailable\n        type: goto\n        stepId: notifyUser\n  \n  - stepId: createBooking\n    # Runs if inventory check succeeded\n  \n  - stepId: notifyUser\n    # Runs if inventory check failed\n\n\nWorking with inputs\n\nSteps need data to do their work, and that data comes from three places. Let’s look at each:\n\n1. Workflow inputs\n\nData passed to the entire workflow gets accessed with $inputs:\n\nworkflows:\n  - workflowId: search-trips\n    inputs:\n      type: object\n      properties:\n        origin:\n          type: string\n        destination:\n          type: string\n    \n    steps:\n      - stepId: search\n        operationId: $sourceDescriptions.api.searchTrips\n        parameters:\n          # Reference workflow inputs\n          - name: origin\n            in: query\n            value: $inputs.origin\n          - name: destination\n            in: query\n            value: $inputs.destination\n\n\n2. Previous step outputs\n\nAccess data from earlier steps.\n\nsteps:\n  - stepId: search\n    operationId: $sourceDescriptions.trainApi.searchTrips\n    outputs:\n      selectedTripId: $response.body#/trips/0/id\n  \n  - stepId: createBooking\n    operationId: $sourceDescriptions.api.createBooking\n    requestBody:\n      payload:\n        # Reference output from previous step\n        tripId: $steps.search.outputs.selectedTripId\n\n\n3. Global parameters\n\nInherit parameters defined at the workflow level:\n\nworkflows:\n  - workflowId: authenticated-flow\n    parameters:\n      - name: Authorization\n        in: header\n        value: Bearer $inputs.token\n    \n    steps:\n      - stepId: getBookingDetails\n        operationId: $sourceDescriptions.trainApi.getBooking\n        # Automatically includes Authorization header\n\n\nParameters\n\nParameters are how you customize API calls. They can go in the URL (query or path), in headers, or in cookies. Each parameter overrides or supplements what’s defined in the OpenAPI operation:\n\nQuery parameters\n\n- stepId: search\n  operationId: $sourceDescriptions.api.searchTrips\n  parameters:\n    - name: origin\n      in: query\n      value: $inputs.origin\n    - name: destination\n      in: query\n      value: $inputs.destination\n    - name: date\n      in: query\n      value: $inputs.departureDate\n    - name: passengers\n      in: query\n      value: $inputs.passengerCount\n\n\nPath parameters\n\n- stepId: getBooking\n  operationId: $sourceDescriptions.api.getBookingById\n  parameters:\n    - name: bookingId\n      in: path\n      value: $steps.createBooking.outputs.bookingId\n\n\nHeader parameters\n\n- stepId: authenticatedRequest\n  operationId: $sourceDescriptions.api.getProtectedResource\n  parameters:\n    - name: Authorization\n      in: header\n      value: Bearer $steps.login.outputs.accessToken\n    - name: X-Request-ID\n      in: header\n      value: $inputs.requestId\n\n\nCookie parameters\n\n- stepId: sessionRequest\n  operationId: $sourceDescriptions.api.getSessionData\n  parameters:\n    - name: sessionId\n      in: cookie\n      value: $steps.auth.outputs.sessionId\n\n\nRequest bodies\n\nFor POST, PUT, PATCH, and QUERY operations, you’ll usually need to send a request body. Arazzo makes this straightforward:\n\nSimple request body\n\n- stepId: addPassenger\n  operationId: $sourceDescriptions.trainApi.addPassengerToBooking\n  requestBody:\n    contentType: application/json\n    payload:\n      name: $inputs.passengerName\n      email: $inputs.passengerEmail\n\n\nComplex request body\n\n- stepId: createBooking\n  operationId: $sourceDescriptions.api.createBooking\n  requestBody:\n    contentType: application/json\n    payload:\n      tripId: $steps.search.outputs.tripId\n      passengers:\n        - name: $inputs.passengers[0].name\n          age: $inputs.passengers[0].age\n          seatPreference: $inputs.passengers[0].seat\n        - name: $inputs.passengers[1].name\n          age: $inputs.passengers[1].age\n          seatPreference: $inputs.passengers[1].seat\n      contactInfo:\n        email: $inputs.email\n        phone: $inputs.phone\n      specialRequests: $inputs.specialRequests\n\n\nUsing previous step data\n\n- stepId: updateBooking\n  operationId: $sourceDescriptions.api.updateBooking\n  parameters:\n    - name: bookingId\n      in: path\n      value: $steps.createBooking.outputs.id\n  requestBody:\n    contentType: application/json\n    payload:\n      # Merge previous booking data with updates\n      status: confirmed\n      paymentId: $steps.processPayment.outputs.id\n      confirmationCode: $steps.generateCode.outputs.code\n\n\nDifferent content types\n\n# JSON\n- stepId: jsonRequest\n  requestBody:\n    contentType: application/json\n    payload:\n      key: value\n\n# Form data\n- stepId: updateTravelPreferences\n  requestBody:\n    contentType: application/x-www-form-urlencoded\n    payload:\n      seatPreference: $inputs.seatPreference\n      mealOption: $inputs.mealOption\n\n# Multipart (file upload)\n- stepId: uploadTicket\n  requestBody:\n    contentType: multipart/form-data\n    payload:\n      file: $inputs.ticketScan\n      bookingId: $inputs.bookingId\n\n\nOutputs\n\nOutputs are how you pluck useful data from responses and make it available to later steps. Don’t extract everything, just grab what you’ll actually need.\n\nBasic outputs\n\n- stepId: createBooking\n  operationId: $sourceDescriptions.api.createBooking\n  outputs:\n    bookingId: $response.body#/id\n    status: $response.body#/status\n\n\nJSON pointer notation\n\nMost real-world APIs using JSON are going to have nested data, with objects and arrays inside each other. Selecting out the exact piece of data needed for an output value can be done with JSON Pointer notation.\n\n- stepId: getBookingDetails\n  operationId: $sourceDescriptions.trainApi.getBooking\n  outputs:\n    bookingId: $response.body#/id\n    customerName: $response.body#/customer/name\n    customerEmail: $response.body#/customer/email\n    tripOrigin: $response.body#/trip/origin\n    tripDestination: $response.body#/trip/destination\n    totalPrice: $response.body#/pricing/total\n\n\nWhen dealing with arrays, you can access a specific record in an array by its index using JSON Pointer notation.\n\n- stepId: search\n  operationId: $sourceDescriptions.api.searchTrips\n  outputs:\n    firstTripPrice: $response.body#/trips/0/price\n    secondTripPrice: $response.body#/trips/1/price\n\n\nHeaders and status\n\nBesides response bodies, you can also extract HTTP status codes and headers. This is useful for capturing rate limits, pagination tokens, or debugging information:\n\n- stepId: makeRequest\n  operationId: $sourceDescriptions.api.someOperation\n  outputs:\n    # Response body\n    responseData: $response.body\n    \n    # Status code\n    statusCode: $statusCode\n    \n    # Headers\n    contentType: $response.header.content-type\n    rateLimit: $response.header.x-rate-limit-remaining\n\n\nUsing outputs in subsequent steps\n\nOutputs are referenced using the $steps runtime expression:\n\nsteps:\n  # Step 1: Create a booking\n  - stepId: createBooking\n    operationId: $sourceDescriptions.api.createBooking\n    outputs:\n      bookingId: $response.body#/id\n      amount: $response.body#/totalPrice\n  \n  # Step 2: Process payment (uses booking outputs)\n  - stepId: processPayment\n    operationId: $sourceDescriptions.paymentApi.createCharge\n    requestBody:\n      payload:\n        bookingReference: $steps.createBooking.outputs.bookingId\n        amount: $steps.createBooking.outputs.amount\n        currency: USD\n    outputs:\n      paymentId: $response.body#/id\n      status: $response.body#/status\n  \n  # Step 3: Confirm booking (uses both previous outputs)\n  - stepId: confirmBooking\n    operationId: $sourceDescriptions.api.confirmBooking\n    parameters:\n      - name: bookingId\n        in: path\n        value: $steps.createBooking.outputs.bookingId\n    requestBody:\n      payload:\n        paymentId: $steps.processPayment.outputs.paymentId\n        paymentStatus: $steps.processPayment.outputs.status\n\n\nBest practices\n\nA few tips that’ll make your workflows easier to work with:\n\nDescriptive step IDs\n\nMake step IDs self-documenting. Six months from now, searchAvailableTrips will be much clearer than step1:\n\n# Good\n- stepId: searchAvailableTrips\n- stepId: selectFirstTrip\n- stepId: createBookingForTrip\n- stepId: processPaymentForBooking\n\n# Avoid\n- stepId: step1\n- stepId: doSomething\n- stepId: api_call\n\n\nExtract useful outputs\n\nBe intentional about outputs. Only extract data you know you’ll need in a later step:\n\n# Good - outputs are used in subsequent steps\n- stepId: search\n  outputs:\n    tripId: $response.body#/trips/0/id  # Used in next step\n    price: $response.body#/trips/0/price  # Used for display\n\n# Avoid - extracting everything \"just in case\"\n- stepId: search\n  outputs:\n    entireResponse: $response.body\n    # Too broad - unclear what will actually be used\n\n# Avoid - vague names\noutputs:\n  id: $response.body#/trips/0/id\n  value: $response.body#/customer/email\n  amount: $response.body#/pricing/total\n\n\nClear descriptions\n\n- stepId: validatePayment\n  description: |\n    Validates payment details before processing.\n    Checks card number format, expiry date, and CVV.\n    Returns validation errors if any field is invalid.\n  operationId: $sourceDescriptions.paymentApi.validateCard\n\n\nWrapping up\n\nSteps are where workflows get real work done. Each step calls an operation, transforms data, and passes results to the next step.\n\nWith steps down, you’re ready to handle the inevitable: things going wrong. The next section covers success and failure conditions."
        },
        {
          "id": "arazzo-v1.0-understanding-structure-workflows",
          "title": "Workflows",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/arazzo/v1.0/understanding-structure/workflows/",
          "content": "What is a workflow?\n  Workflow structure    \n      Required fields\n      Optional fields\n    \n  \n  Workflow inputs    \n      Basic inputs\n      Required vs optional\n      Complex input types\n      Using inputs in steps\n    \n  \n  Workflow outputs    \n      Simple outputs\n      Computed outputs\n    \n  \n  Global parameters\n  Workflow dependencies\n  Workflow patterns    \n      Linear sequential workflows\n      Multi-source workflows\n      Nested workflows\n    \n  \n  Best practices    \n      Descriptive workflow IDs\n      Write summaries that add value\n      Document complex workflows\n      Keep workflows focused\n    \n  \n\n\nThink 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.\n\nWhat is a workflow?\n\nA 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.\n\nworkflows:\n  - workflowId: book-train-ticket\n    summary: Complete workflow for booking a train ticket\n    description: |\n      This workflow handles the end-to-end process of booking a train ticket:\n      1. Search for available trips\n      2. Select a trip\n      3. Create a booking\n      4. Process payment\n      5. Receive confirmation\n    \n    inputs:\n      type: object\n      properties:\n        origin:\n          type: string\n        destination:\n          type: string\n        departureDate:\n          type: string\n          format: date\n        passengers:\n          type: array\n    \n    steps:\n      # ... step definitions\n    \n    outputs:\n      bookingId: $steps.createBooking.outputs.bookingId\n      ticketUrl: $steps.confirm.outputs.ticketUrl\n\n\nWorkflow structure\n\nRequired fields\n\nOnly two fields are actually required to create a workflow:\n\nworkflowId 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.\n\nworkflows:\n  - workflowId: create-and-retrieve-booking\n\n\nsteps 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.\n\nworkflows:\n  - workflowId: simple-workflow\n    steps:\n      - stepId: firstStep\n        # ... step configuration\n      - stepId: secondStep\n        # ... step configuration\n\n\nOptional fields\n\nsummary - A brief description:\n\nworkflows:\n  - workflowId: book-ticket\n    summary: Search for and book a train ticket\n\n\ndescription - 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.\n\nworkflows:\n  - workflowId: complex-booking\n    summary: Multi-step booking process\n    description: |\n      This workflow handles complex booking scenarios including:\n      \n      - Multi-city travel\n      - Group bookings with different passenger types\n      - Seat selection and preferences\n      - Special assistance requests\n      - Payment plan options\n      \n      The workflow includes extensive error handling for:\n      - Sold out trips\n      - Payment failures\n      - Booking timeouts\n\n\ninputs - Define what data the workflow accepts using a JSON Schema object:\n\nworkflows:\n  - workflowId: search-and-book\n    inputs:\n      type: object\n      required:\n        - origin\n        - destination\n      properties:\n        origin:\n          type: string\n          description: Departure station code\n          example: BOS\n        destination:\n          type: string\n          description: Arrival station code\n          example: NYC\n        departureDate:\n          type: string\n          format: date\n          description: Date of travel\n\n\noutputs - Define what data the workflow produces:\n\nworkflows:\n  - workflowId: book-ticket\n    outputs:\n      bookingId:\n        $steps.createBooking.outputs.bookingId\n      confirmationCode:\n        $steps.confirm.outputs.code\n      totalPrice:\n        $steps.payment.outputs.amount\n\n\nparameters - Global parameters that apply to all steps (unless overridden):\n\nworkflows:\n  - workflowId: authenticated-workflow\n    parameters:\n      - name: Authorization\n        in: header\n        value: Bearer $inputs.token\n    \n    steps:\n      # All steps will include this Authorization header\n      # unless they override it\n\n\ndependsOn - Specify dependencies on other workflows:\n\nworkflows:\n  - workflowId: authenticated-booking\n    dependsOn:\n      - authenticate-user\n    steps:\n      # This workflow assumes authentication has been completed\n\n\nWorkflow inputs\n\nWorkflows 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.\n\nBasic inputs\n\nworkflows:\n  - workflowId: search-trips\n    inputs:\n      type: object\n      properties:\n        origin:\n          type: string\n        destination:\n          type: string\n        date:\n          type: string\n          format: date\n\n\nInput 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.\n\nRequired vs optional\n\nSome inputs are must-haves (where are you going?), while others can have sensible defaults (how many passengers? probably just one).\n\nThe required array specifies which inputs must be provided, everything else is assumed to be optional, with default values where specified:\n\nworkflows:\n  - workflowId: flexible-search\n    inputs:\n      type: object\n      required:\n        - origin\n        - destination\n      properties:\n        origin:\n          type: string\n        destination:\n          type: string\n        departureDate:\n          type: string\n          format: date\n          description: Optional - defaults to today\n        passengers:\n          type: integer\n          default: 1\n          minimum: 1\n          maximum: 9\n\n\nComplex input types\n\nInputs can be as simple or complex as you need. Nested objects, arrays, enums, JSON Schema has a keyword for pretty much everything.\n\nworkflows:\n  - workflowId: book-with-preferences\n    inputs:\n      type: object\n      properties:\n        passengers:\n          type: array\n          items:\n            type: object\n            properties:\n              name:\n                type: string\n              age:\n                type: integer\n              seatPreference:\n                type: string\n                enum: [window, aisle, any]\n        \n        paymentMethod:\n          type: object\n          properties:\n            type:\n              type: string\n              enum: [credit_card, debit_card, paypal]\n            token:\n              type: string\n\n\nUsing inputs in steps\n\nOnce 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:\n\nworkflows:\n  - workflowId: search-workflow\n    inputs:\n      type: object\n      properties:\n        origin:\n          type: string\n        destination:\n          type: string\n    \n    steps:\n      - stepId: search\n        operationId: $sourceDescriptions.api.searchTrips\n        parameters:\n          - name: origin\n            in: query\n            value: $inputs.origin  # Reference input\n          - name: destination\n            in: query\n            value: $inputs.destination  # Reference input\n\n\nWorkflow outputs\n\nWorkflows 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.\n\nSimple outputs\n\nworkflows:\n  - workflowId: create-booking\n    steps:\n      - stepId: book\n        # ... creates a booking\n        outputs:\n          bookingId: $response.body#/id\n    \n    outputs:\n      bookingId: $steps.book.outputs.bookingId\n      createdAt: $steps.book.outputs.timestamp\n\n\nComputed outputs\n\nworkflows:\n  - workflowId: calculate-total\n    steps:\n      - stepId: getBasePrice\n        outputs:\n          basePrice: $response.body#/price\n      \n      - stepId: getTax\n        outputs:\n          taxAmount: $response.body#/tax\n    \n    outputs:\n      basePrice: $steps.getBasePrice.outputs.basePrice\n      tax: $steps.getTax.outputs.taxAmount\n      total: $steps.getBasePrice.outputs.basePrice + $steps.getTax.outputs.taxAmount\n\n\nGlobal parameters\n\nGot 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:\n\nworkflows:\n  - workflowId: api-with-auth\n    parameters:\n      # This header will be included in all step requests\n      - name: Authorization\n        in: header\n        value: Bearer $inputs.apiKey\n      \n      # API version header\n      - name: API-Version\n        in: header\n        value: \"2024-01\"\n    \n    steps:\n      - stepId: getUser\n        operationId: $sourceDescriptions.api.getUser\n        # Automatically includes Authorization and API-Version headers\n      \n      - stepId: updateUser\n        operationId: $sourceDescriptions.api.updateUser\n        # Also automatically includes both headers\n\n\nWorkflow dependencies\n\nWhen 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:\n\nworkflows:\n  # Base workflow - no dependencies\n  - workflowId: authenticate\n    summary: Get an authentication token\n    steps:\n      - stepId: login\n        outputs:\n          token: $response.body#/access_token\n  \n  # Depends on authentication\n  - workflowId: get-user-bookings\n    dependsOn:\n      - authenticate\n    summary: Retrieve bookings for authenticated user\n    steps:\n      - stepId: fetchBookings\n        parameters:\n          - name: Authorization\n            in: header\n            value: Bearer $inputs.token  # Assumes token from authenticate workflow\n\n\nSmart tools can use this information to automatically run prerequisite workflows, validate execution order, or generate nice workflow diagrams.\n\nWorkflow patterns\n\nHere are some common patterns for organizing workflows:\n\nLinear sequential workflows\n\nThe most straightforward pattern - steps execute one after another:\n\nworkflows:\n  - workflowId: linear-booking\n    steps:\n      - stepId: searchTrips\n      - stepId: selectTrip\n      - stepId: createBooking\n      - stepId: processPayment\n\n\nMulti-source workflows\n\nWorkflows can orchestrate across multiple APIs by referencing operations from different source descriptions:\n\nworkflows:\n  - workflowId: complete-order\n    steps:\n      - stepId: checkInventory\n        operationId: $sourceDescriptions.inventoryApi.checkStock\n      \n      - stepId: processPayment\n        operationId: $sourceDescriptions.paymentApi.createCharge\n      \n      - stepId: scheduleShipping\n        operationId: $sourceDescriptions.shippingApi.createShipment\n\n\nNested workflows\n\nBreak complex processes into smaller, reusable workflows:\n\nworkflows:\n  - workflowId: authenticate\n    summary: Get authentication token\n    steps:\n      - stepId: login\n        operationId: $sourceDescriptions.authApi.login\n  \n  - workflowId: authenticated-booking\n    dependsOn:\n      - authenticate\n    steps:\n      - stepId: getProfile\n        operationId: $sourceDescriptions.userApi.getProfile\n      \n      - stepId: createBooking\n        operationId: $sourceDescriptions.bookingApi.create\n\n\nBest practices\n\nHere’s a few tips for writing workflows that are easy to understand and maintain.\n\nDescriptive workflow IDs\n\nPick workflow IDs that clearly describe what the workflow does:\n\n# Good - clear and descriptive\n- workflowId: search-and-book-train-ticket\n- workflowId: cancel-booking-with-refund\n- workflowId: update-passenger-details\n\n# Avoid - vague or cryptic\n- workflowId: workflow1\n- workflowId: process\n- workflowId: sab\n\n\nWrite summaries that add value\n\nDon’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:\n\n# Good\n- workflowId: guest-checkout\n  summary: Complete checkout process for guest users without registration\n\n# Avoid\n- workflowId: checkout\n  summary: Checkout workflow\n\n\nDocument complex workflows\n\nFor workflows with many steps or intricate logic, use the description field to explain the overall process, key decisions, and error handling:\n\n- workflowId: multi-city-booking\n  summary: Book a multi-city train journey\n  description: |\n    This workflow handles the complexity of booking trips across multiple cities:\n    \n    **Process:**\n    1. Validates that all cities form a valid route\n    2. Searches for available trips for each leg\n    3. Ensures compatible connection times\n    4. Creates individual bookings for each leg\n    5. Links bookings together\n    6. Processes single payment for entire journey\n    \n    **Error Handling:**\n    - If any leg is unavailable, the entire booking fails\n    - If payment fails, all leg bookings are cancelled\n    - Connection validation ensures minimum 30 minute layovers\n\n\nKeep workflows focused\n\nEach workflow should do one thing well. Don’t create a mega-workflow that handles everything from user registration to coffee machine calibration:\n\n# Good - focused workflows\n- workflowId: search-trips\n- workflowId: create-booking\n- workflowId: process-payment\n- workflowId: cancel-booking\n\n# Avoid - overly broad workflow\n- workflowId: do-everything\n  # ... handles search, booking, payment, cancellation, refunds, ...\n\n\nSteps 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."
        },
        {
          "id": "arazzo-v1.0-working-with-arazzo-runtime-expressions",
          "title": "Runtime expressions",
          "collection": {
            "label": "specifications",
            "name": "Specifications"
          },
          "categories": "",
          "tags": "",
          "url": "/arazzo/v1.0/working-with-arazzo/runtime-expressions/",
          "content": "What are runtime expressions?\n  Expression contexts    \n      Workflow inputs\n      Previous step outputs\n      Current response\n      HTTP status\n      Source descriptions\n    \n  \n  Operators in conditions    \n      Comparison operators\n      Logical operators\n      String operators\n      Null Safety\n    \n  \n  Extracting values\n  Examples    \n      Status code checks\n      Field value checks\n      Checking response structure\n      Header checks\n      JSONPath in conditions\n    \n  \n\n\nRuntime expressions are the heart of Arazzo’s dynamic capabilities. They allow you to reference data from inputs, previous steps, and responses, compute new values, and create conditional logic. Understanding runtime expressions is essential for building sophisticated workflows.\n\nWhat are runtime expressions?\n\nRuntime expressions are evaluated during workflow execution to produce values. They start with a $ and use dot notation to access data:\n\n# Reference workflow input\n$inputs.origin\n\n# Reference previous step output\n$steps.search.outputs.tripId\n\n# Reference response status\n$statusCode\n\n# Reference response body (The body payload needs to be accessed with a JSON pointer)\n$response.body#/booking/id\n\n\nExpression contexts\n\nExpressions can reference data from several contexts:\n\nWorkflow inputs\n\nAccess data passed to the workflow:\n\nworkflows:\n  - workflowId: search-trips\n    inputs:\n      type: object\n      properties:\n        origin:\n          type: string\n        destination:\n          type: string\n        date:\n          type: string\n    \n    steps:\n      - stepId: search\n        parameters:\n          - name: from\n            in: query\n            value: $inputs.origin  # Access workflow input\n          - name: to\n            in: query\n            value: $inputs.destination\n          - name: departure\n            in: query\n            value: $inputs.date\n\n\nNested inputs:\n\ninputs:\n  type: object\n  properties:\n    passenger:\n      type: object\n      properties:\n        name:\n          type: string\n        age:\n          type: integer\n\n# Access nested values\nvalue: $inputs.passenger.name\nvalue: $inputs.passenger.age\n\n\nArray inputs:\n\ninputs:\n  type: array\n  items:\n    properties:\n      userName:\n        type: string\n      email:\n        type: string\n\n# Access input properties\nvalue: $inputs[0].userName\nvalue: $inputs[0].email\n\n\nPrevious step outputs\n\nReference outputs from earlier steps:\n\nsteps:\n  - stepId: search\n    operationId: $sourceDescriptions.api.searchTrips\n    outputs:\n      selectedTripId: $response.body#/trip/id\n      tripPrice: $response.body#/trip/price\n  \n  - stepId: createBooking\n    operationId: $sourceDescriptions.api.createBooking\n    requestBody:\n      payload:\n        # Reference outputs from 'search' step\n        tripId: $steps.search.outputs.selectedTripId\n        price: $steps.search.outputs.tripPrice\n\n\nTo access an output from a previous step, use the syntax:\n\n$steps.{stepId}.outputs.{outputName}\n\n\nFor this to work the output must be defined in the referenced step, and that step must have already run.\n\nLearn more about steps and outputs.\n\nCurrent response\n\nWithin success/failure criteria and outputs, access the current response:\n\n- stepId: getBooking\n  operationId: $sourceDescriptions.api.getBooking\n  successCriteria:\n    # Access response body\n    - condition: $response.body#/status == 'confirmed'\n\n    # Access response headers\n    - condition: $response.header.content-type == 'application/json'\n\n  outputs:\n    # Extract from response\n    bookingId: $response.body#/id\n    customerEmail: $response.body#/customer/email\n    total: $response.body#/pricing/total\n\n\nResponse properties:\n\n  $response.body - Response body (parsed JSON/XML)\n  $response.header - Response headers object\n  $response.header.content-type - Specific header\n\n\nHTTP status\n\nAccess the HTTP status code of the current response:\n\nsuccessCriteria:\n  - condition: $statusCode &gt;= 200 &amp;&amp; $statusCode &lt; 300\n\noutputs:\n  responseCode: $statusCode\n\n\nSource descriptions\n\nReference operations from source APIs:\n\nsourceDescriptions:\n  - name: trainApi\n    url: ./train-api.yaml\n    type: openapi\n\nsteps:\n  - stepId: search\n    # Reference operation from source\n    operationId: $sourceDescriptions.trainApi.searchTrips\n\n\nThat’s $sourceDescriptions.{sourceName}.{operationId} or  $sourceDescriptions.{sourceName}.{workflowId} for other Arazzo workflows.\n\nOperators in conditions\n\nRuntime expressions support various operators for use in success and failure criteria:\n\nComparison operators\n\nsuccessCriteria:\n  # Equality\n  - condition: $response.body#/status == 'confirmed'\n  - condition: $statusCode == 200\n  \n  # Inequality\n  - condition: $response.body#/error != null\n  - condition: $statusCode != 404\n  \n  # Greater than\n  - condition: $response.body#/price &gt; 100\n  - condition: $response.body#/stock &gt; $inputs.quantity\n  \n  # Greater than or equal\n  - condition: $response.body#/rating &gt;= 4.0\n  \n  # Less than\n  - condition: $response.body#/price &lt; $inputs.maxPrice\n  \n  # Less than or equal\n  - condition: $response.body#/quantity &lt;= $response.body#/maxQuantity\n\n\nLogical operators\n\nsuccessCriteria:\n  # AND - all conditions must be true\n  - condition: $statusCode == 200 &amp;&amp; $response.body#/available == true\n  - condition: $response.body#/price &gt; 0 &amp;&amp; $response.body#/price &lt; 1000\n  \n  # OR - at least one condition must be true\n  - condition: $statusCode == 200 || $statusCode == 201\n  - condition: $response.body#/status == 'confirmed' || $response.body#/status == 'pending'\n\n\nString operators\n\nConcat strings using interpolation:\n\noutputs:\n  fullName: '{$response.body#/firstName} {$response.body#/lastName}'\n  message: 'Booking {$response.body#/id} confirmed'\n\n\nNull Safety\n\nCheck for null values in conditions:\n\nsuccessCriteria:\n  # Check for null/undefined\n  - condition: $response.body#/email != null\n  - condition: $response.body#/address != null\n  - condition: $response.body#/id != null\n\n\nExtracting values\n\nUse outputs to extract values from responses:\n\noutputs:\n  # Extract simple values\n  bookingId: $response.body#/id\n  customerEmail: $response.body#/customer/email\n  tripPrice: $response.body#/trip/price\n  \n  # Extract nested values\n  originCity: $response.body#/trip/origin/city\n  destinationCity: $response.body#/trip/destination/city\n\n\nExamples\n\nExpressions are frequently used in success and failure criteria. Here’s a comprehensive guide to condition patterns:\n\nStatus code checks\n\n  # Exact status\n  - condition: $statusCode == 200\n  - condition: $statusCode == 201\n  \n  # Status ranges\n  - condition: $statusCode &gt;= 200 &amp;&amp; $statusCode &lt; 300\n  \n  # Multiple acceptable codes\n  - condition: $statusCode == 200 || $statusCode == 201\n\n\nField value checks\n\n# Simple field checks\n- condition: $response.body#/status == 'confirmed'\n- condition: $response.body#/stock &gt; 0\n- condition: $response.body#/payment/processed == true\n\n# Field existence\n- condition: $response.body#/id != null\n- condition: $response.body#/bookingReference != null\n\n\nChecking response structure\n\n# Check for required object properties\n- condition: $response.body#/trip != null\n- condition: $response.body#/trip/available == true\n- condition: $response.body#/trip/price &lt;= $inputs.maxPrice\n\n\nHeader checks\n\n  # Header values\n  - condition: $response.header['content-type'] == 'application/json'\n  - condition: $response.header['x-api-version'] == '2024-01'\n  \n  # Rate limiting (header values are strings)\n  - condition: $response.header['x-rate-limit-remaining'] != '0'\n  - condition: $response.header['retry-after'] == null\n\n\nJSONPath in conditions\n\nFor complex filtering, use JSONPath expressions with type: jsonpath. This is only available in conditions (successCriteria/failureCriteria), not in outputs or parameters.\n\n\n# Check all bookings are confirmed\n- type: jsonpath\n  context: $response.body\n  condition: $.bookings[?(@.status != 'confirmed')][0] == null\n\n# At least one affordable trip\n- type: jsonpath\n  context: $response.body\n  condition: $.trips[?(@.price &lt; 100)][0] != null\n\n# Find child passengers\n- type: jsonpath\n  context: $response.body\n  condition: $.passengers[?(@.age &lt; 18)][0] != null\n\n# No critical errors\n- type: jsonpath\n  context: $response.body\n  condition: $.errors[?(@.severity == 'critical')][0] == null\n\n\nMastering runtime expressions enables you to build dynamic, data-driven workflows that respond intelligently to API responses and user inputs."
        },
        {
          "id": "404",
          "title": "404",
          "collection": {
            "label": "pages",
            "name": "Posts"
          },
          "categories": "",
          "tags": "",
          "url": "/404",
          "content": "Sorry, you page you were looking for does not exist."
        },
        {
          "id": "500",
          "title": "500",
          "collection": {
            "label": "pages",
            "name": "Posts"
          },
          "categories": "",
          "tags": "",
          "url": "/500",
          "content": "500\n\nInternal Server Error :(\n\nThe requested page could not be delivered."
        },
        {
          "id": "about",
          "title": "About",
          "collection": {
            "label": "pages",
            "name": "Posts"
          },
          "categories": "",
          "tags": "",
          "url": "/about/",
          "content": "This is the basic Bridgetown site template. You can find out more info about customizing your Bridgetown site, as well as basic Bridgetown usage documentation at bridgetownrb.com\n\nYou can find the source code for Bridgetown at GitHub:\nbridgetownrb /\nbridgetown"
        },
        {
          "id": "",
          "title": "Bump.sh Docs & Guides",
          "collection": {
            "label": "data",
            "name": "Posts"
          },
          "categories": "",
          "tags": "",
          "url": "",
          "content": ""
        },
          {
            "id": "guides",
            "title": "Guides",
            "categories": "",
            "tags": "",
            "url": "/guides/",
            "content": "Discover the reasons behind the creation of OpenAPI, its capabilities and how to make a good OpenAPI document, from design to maintenance.\n  \n\n  \n      Get an in-depth overview of Arazzo, and learn how to design and maintain an Arazzo document that can be used to automate real workflows."
          },
          {
            "id": "guides-page-2",
            "title": "Guides (Page 2)",
            "categories": "",
            "tags": "",
            "url": "/guides/page/2/",
            "content": "Discover the reasons behind the creation of OpenAPI, its capabilities and how to make a good OpenAPI document, from design to maintenance.\n  \n\n  \n      Get an in-depth overview of Arazzo, and learn how to design and maintain an Arazzo document that can be used to automate real workflows."
          },
          {
            "id": "guides-page-3",
            "title": "Guides (Page 3)",
            "categories": "",
            "tags": "",
            "url": "/guides/page/3/",
            "content": "Discover the reasons behind the creation of OpenAPI, its capabilities and how to make a good OpenAPI document, from design to maintenance.\n  \n\n  \n      Get an in-depth overview of Arazzo, and learn how to design and maintain an Arazzo document that can be used to automate real workflows."
          },
          {
            "id": "guides-page-4",
            "title": "Guides (Page 4)",
            "categories": "",
            "tags": "",
            "url": "/guides/page/4/",
            "content": "Discover the reasons behind the creation of OpenAPI, its capabilities and how to make a good OpenAPI document, from design to maintenance.\n  \n\n  \n      Get an in-depth overview of Arazzo, and learn how to design and maintain an Arazzo document that can be used to automate real workflows."
          },
          {
            "id": "guides-page-5",
            "title": "Guides (Page 5)",
            "categories": "",
            "tags": "",
            "url": "/guides/page/5/",
            "content": "Discover the reasons behind the creation of OpenAPI, its capabilities and how to make a good OpenAPI document, from design to maintenance.\n  \n\n  \n      Get an in-depth overview of Arazzo, and learn how to design and maintain an Arazzo document that can be used to automate real workflows."
          },
          {
            "id": "guides-page-6",
            "title": "Guides (Page 6)",
            "categories": "",
            "tags": "",
            "url": "/guides/page/6/",
            "content": "Discover the reasons behind the creation of OpenAPI, its capabilities and how to make a good OpenAPI document, from design to maintenance.\n  \n\n  \n      Get an in-depth overview of Arazzo, and learn how to design and maintain an Arazzo document that can be used to automate real workflows."
          },
          {
            "id": "product-updates",
            "title": "Product Updates",
            "categories": "",
            "tags": "",
            "url": "/product-updates/",
            "content": "\">"
          },
          {
            "id": "product-updates-page-2",
            "title": "Product Updates (Page 2)",
            "categories": "",
            "tags": "",
            "url": "/product-updates/page/2/",
            "content": "\">"
          },
          {
            "id": "product-updates-page-3",
            "title": "Product Updates (Page 3)",
            "categories": "",
            "tags": "",
            "url": "/product-updates/page/3/",
            "content": "\">"
          },
          {
            "id": "product-updates-page-4",
            "title": "Product Updates (Page 4)",
            "categories": "",
            "tags": "",
            "url": "/product-updates/page/4/",
            "content": "\">"
          },
          {
            "id": "product-updates-page-5",
            "title": "Product Updates (Page 5)",
            "categories": "",
            "tags": "",
            "url": "/product-updates/page/5/",
            "content": "\">"
          },
          {
            "id": "product-updates-page-6",
            "title": "Product Updates (Page 6)",
            "categories": "",
            "tags": "",
            "url": "/product-updates/page/6/",
            "content": "\">"
          },
          {
            "id": "product-updates-page-7",
            "title": "Product Updates (Page 7)",
            "categories": "",
            "tags": "",
            "url": "/product-updates/page/7/",
            "content": "\">"
          },
          {
            "id": "product-updates-page-8",
            "title": "Product Updates (Page 8)",
            "categories": "",
            "tags": "",
            "url": "/product-updates/page/8/",
            "content": "\">"
          },
          {
            "id": "guides-technical-writing",
            "title": "Guides in category Technical Writing",
            "categories": "",
            "tags": "",
            "url": "/guides/technical-writing/",
            "content": ""
          },
          {
            "id": "guides-technical-writing-page-2",
            "title": "Guides in category Technical Writing (Page 2)",
            "categories": "",
            "tags": "",
            "url": "/guides/technical-writing/page/2/",
            "content": ""
          },
          {
            "id": "guides-bump-sh-tutorials",
            "title": "Guides in category Bump Sh Tutorials",
            "categories": "",
            "tags": "",
            "url": "/guides/bump-sh-tutorials/",
            "content": ""
          },
          {
            "id": "guides-bump-sh-tutorials-page-2",
            "title": "Guides in category Bump Sh Tutorials (Page 2)",
            "categories": "",
            "tags": "",
            "url": "/guides/bump-sh-tutorials/page/2/",
            "content": ""
          },
          {
            "id": "guides-openapi",
            "title": "Guides in category Openapi",
            "categories": "",
            "tags": "",
            "url": "/guides/openapi/",
            "content": ""
          },
          {
            "id": "guides-openapi-page-2",
            "title": "Guides in category Openapi (Page 2)",
            "categories": "",
            "tags": "",
            "url": "/guides/openapi/page/2/",
            "content": ""
          },
          {
            "id": "guides-api-basics",
            "title": "Guides in category Api Basics",
            "categories": "",
            "tags": "",
            "url": "/guides/api-basics/",
            "content": ""
          },
          {
            "id": "guides-asyncapi",
            "title": "Guides in category Asyncapi",
            "categories": "",
            "tags": "",
            "url": "/guides/asyncapi/",
            "content": ""
          }
]
