Loading

Organize and annotate your API docs

This page explains how to organize your API documentation and add technical metadata to help users understand your APIs. You'll find guidance on:

  • Adding required OpenAPI document information
  • Adding structure with operation IDs and tags
  • Documenting API availability and lifecycle status
  • Specifying required permissions and privileges
  • Extending OpenAPI with custom properties

For guidance on writing effective API content like summaries, descriptions, and examples, see Core guidelines.

The published OpenAPI documents must have the following metadata in the info object:

Field Description
description A descriptive overview of the API's purpose and functionality. This should be a concise summary that helps users understand what the API does.
license License information for the API (which is distinct from the documentation license).
Until we have a custom extension, add the documentation license (Deed - Attribution-NonCommercial-NoDerivatives 4.0 International - Creative Commons) to info.description
title The title of the API
version The version of the document (which is distinct from the OpenAPI specification version and the API implementation version)
x-feedbackLink This specialization extension enables us to collect feedback about the document. The URL should take readers to a GitHub issue template so they can create an issue. Refer to Get feedback from users

You must specify the version number of the OpenAPI specification that the OpenAPI document uses. This value is not related to the API version string in the document info.

Bump.sh supports all versions from Swagger 2.0 to OpenAPI 3.1.

Learn more about the OpenAPI object.

The operationId uniquely identifies the operation.

It is case sensitive and must be unique in the OpenAPI document.

When adding operation identifiers:

  • Ensure each operation has a unique operationId
  • Make operationIds valid for use in URLs (no special characters)
  • Use consistent naming patterns across related operations

We use tags to group APIs by feature.

There are two types of tags:

Tag type Purpose Notes
Document-level Define the available tag categories for the entire API - Use human-readable names
- Add descriptions
- Order affects navigation
Operation-level Assign each endpoint to a category - Use tags from document-level list
- Each operation needs at least one tag

Best practices:

  • Use consistent tag names across your entire API
  • Use one tag per operation for cleaner navigation
  • Use sentence case for tag names (use x-displayName to override the actual tag name if necessary)
  • Order tags logically to create intuitive navigation flow
paths:
  /indices:
    post:
      summary: Create an index
      tags: ["Index management"]
      description: Creates a new index with optional settings and mappings
  /indices/{index}:
    delete:
      summary: Delete an index
      tags: ["Index management"]
      description: Removes an index and all its data
  /indices/{index}/_settings:
    put:
      summary: Update index settings
      tags: ["Index management"]
      description: Modifies settings for an existing index

Define tag metadata in the top-level tags section. Write a description for each tag to clarify its purpose:

tags:
  - name: "Index management"
    description: Operations for creating, configuring, and managing indices
  - name: "Search"
    description: Operations for querying and retrieving documents
  - name: "Document operations"
    description: Operations for creating, updating, and deleting individual documents

Default namespace behavior

The compiler extracts the namespace from your endpoint name using everything before the first dot:

  • indices.create"indices" tag
  • search.template"search" tag
  • cluster.health"cluster" tag
  • ping"ping" tag (no dot, uses full name)

This automatic grouping works well when your namespace matches how users think about the API functionality.

Overriding defaults with @doc_tag

Use the @doc_tag annotation when the automatic namespace doesn't match user expectations or when you want more descriptive groupings:

/**
 * @rest_spec_name indices.create
 * @doc_tag "Index management"
 */
export interface Request extends RequestBase {
  // ...
}

This moves the operation from an "indices" section to an "Index management" section.

Add new tags

If you create a new tag value, you must also add it to the elasticsearch-shared-overlays.yaml file. You can see all existing tag values in that file.

Use sentence case for tag names. If you need different casing in the final docs, use the x-displayName extension in the overlay file.

Note

Each operation can only have one tag, even though the OpenAPI specification supports multiple tags. This is a deliberate system constraint to keep the docs navigation clean and predictable.

Per OpenAPI Specification v3.0.3:

"While the OpenAPI Specification tries to accommodate most use cases, additional data can be added to extend the specification at certain points."

The extensions properties are implemented as patterned fields that are always prefixed by x-. The following extensions are supported by Bump.sh:

Field Description
x-beta Indicates that something is in beta. Note: Use x-state instead.
x-codeSamples Defines code examples and their programming language. Docs
x-displayName Overrides tag names at the document level to align with documentation standards.
x-feedbackLink Adds a link for users to send feedback. Docs
x-model Used to abbreviate deeply nested/recursive API sections (e.g., Elasticsearch query DSL). Should include an externalDocs link. Currently only applied via overlays.
x-state Indicates lifecycle state (e.g., “Technical preview; added in 9.1.0”). Appears next to the operation/property.
x-topics Adds extra pages shown below the introduction. Docs
Note

For more information, refer to the Bump.sh page about Extensions

In OpenAPI, lifecycle status is communicated through extension properties like x-state and the standard deprecated field. These annotations indicate deployment compatibility, stability level, and version information.

Availability and stability is specified using the x-state extension property:

paths:
  /indices/{index}:
    get:
      summary: Get index information
      x-state: "Generally available; Added in 7.10.0"
      # ...

  /ml/anomaly_detection/{jobId}/_forecast:
    post:
      summary: Create anomaly detection forecast
      x-state: "Beta; Added in 8.5.0"
      # ...

Deprecation notices use the standard OpenAPI deprecated boolean field:

components:
  schemas:
    IndexSettings:
      type: object
      properties:
        blocks:
          type: object
          deprecated: true
          description: |
            Deprecated in 8.0.0. Use 'index.blocks.read_only' instead.
            Controls read/write blocks on the index.

API availability is specified using the @availability annotation:

/**
 * @availability stack
 * @availability serverless
 */
class FooRequest {
  bar: string
  /**
   * @availability stack
   * @availability serverless
   */
  baz: string
  faz: string
}

When to use parameter/property-level availability

Parameter and property-level @availability annotations are necessary in specific scenarios:

  1. Mixed deployment availability: When an API supports both serverless and stateful deployments, but some parameters/properties are only available in one deployment type
  2. Later additions: When a parameter or property is added after the initial API release, making its availability later than the API-level availability

API stability is indicated using the stability parameter:

/**
 * @availability stack since=8.0.0 stability=beta
 * @availability serverless stability=experimental
 */
class BetaRequest {
  // ...
}

The stability values map to the following x-state labels:

  • stability=stable (default) → "Generally available"
  • stability=beta → "Beta"
  • stability=experimental → "Technical preview"

Deployment-specific APIs can be restricted to certain deployment types:

export class Example {
  /**
   * Available in both (default when no annotations)
   */
  fieldBoth1: integer

  /**
   * @availability serverless
   */
  fieldServerlessOnly: integer

  /**
   * @availability stack
   */
  fieldStackOnly: integer
}

This example shows a mixed deployment scenario where the API itself is available to both deployment types, but individual parameters have different availability constraints.

Version information uses the since= parameter:

/**
 * @availability stack since=7.10.0
 * @availability serverless
 */
class FooRequest {
  /**
   * Added with the original API
   */
  originalField: string

  /**
   * @availability stack since=7.11.0
   * @availability serverless
   */
  laterAddition: string
}

This example shows the "later addition" scenario where laterAddition was added after the initial API release, requiring a parameter-level availability annotation with a later version than the API-level availability.

Deprecation notices use the @deprecated annotation:

class Foo {
  bar: string
  /** @deprecated 7.0.0 */
  baz?: string
  faz: string
}

You can add an optional description explaining the deprecation:

class Foo {
  bar: string
  /** @deprecated 7.0.0 'baz' has been deprecated, use 'bar' instead */
  baz?: string
  faz: string
}
Important

Since we now only publish API docs for major versions (v8, v9) and not for minor versions (8.1, 8.2, etc.), always include the full version information in your x-state labels. For example: `Technical preview; added in 9.1.0".

Required permissions aren't part of the standard OpenAPI specification, but they can be documented in operation descriptions or through custom extensions. In Elasticsearch, we use annotations that generate structured permission documentation.

Add permission requirements to the operation description using markdown formatting:

paths:
  /indices/{index}:
    put:
      summary: Create index
      description: |
        Creates a new index with optional settings and mappings.

        ## Required authorization

        * Index privileges: `create_index`, `manage`

For APIs requiring multiple permission types:

paths:
  /indices/{index}/_stats:
    get:
      summary: Get index statistics
      description: |
        Returns statistics about one or more indices.

        ## Required authorization

        * Index privileges: `monitor`, `view_index_metadata`
        * Cluster privileges: `monitor`

Index-level permissions use the @index_privileges annotation:

/**
 * @rest_spec_name indices.create
 * @availability stack since=0.0.0 stability=stable
 * @index_privileges create_index, manage
 */
export interface Request extends RequestBase {
 // ...
}

This generates the following markdown section in the operation description:

## Required authorization

* Index privileges: `create_index`, `manage`

Cluster-level permissions use the @cluster_privileges annotation:

/**
 * @rest_spec_name cluster.state
 * @availability stack since=1.3.0 stability=stable
 * @cluster_privileges monitor, manage
 */
export interface Request extends RequestBase {
 // ...
}

This adds:

## Required authorization

* Cluster privileges: `monitor`, `manage`

Combined privileges use both annotations together:

/**
 * @rest_spec_name indices.analyze
 * @index_privileges read
 * @cluster_privileges monitor
 */
export interface Request extends RequestBase {
 // ...
}

This creates:

## Required authorization

* Index privileges: `read`
* Cluster privileges: `monitor`
Note

The annotation system doesn't support "OR" relationships between privileges. When multiple privileges are listed, they appear as if all are required. Use only the minimum necessary privileges for each operation to avoid confusion.