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
operationId
s 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"
tagsearch.template
→"search"
tagcluster.health
→"cluster"
tagping
→"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.
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 |
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:
- Mixed deployment availability: When an API supports both serverless and stateful deployments, but some parameters/properties are only available in one deployment type
- 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
}
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`
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.