OpenAPI specification generation
Ktor provides support for building OpenAPI specifications at runtime from one or more documentation sources.
This functionality is available through:
The OpenAPI compiler extension (included in the Ktor Gradle plugin), which analyzes routing code at compile time and generates Kotlin code that registers OpenAPI metadata at runtime.
The routing annotation runtime API, which attaches OpenAPI metadata directly to routes in the running application.
You can use one or both and combine them with the OpenAPI and SwaggerUI plugins to serve interactive API documentation.
Add dependencies
To enable OpenAPI metadata generation, apply the Ktor Gradle plugin to your project:
To use runtime route annotations, add the
ktor-server-routing-openapiartifact to your build script:implementation("io.ktor:ktor-server-routing-openapi:$ktor_version")implementation "io.ktor:ktor-server-routing-openapi:$ktor_version"<dependency> <groupId>io.ktor</groupId> <artifactId>ktor-server-routing-openapi-jvm</artifactId> <version>${ktor_version}</version> </dependency>
Configure the OpenAPI compiler extension
The OpenAPI compiler extension controls how routing metadata is collected at compile time. It does not define the final OpenAPI document itself.
During compilation, the plugin generates Kotlin code that uses the OpenAPI runtime API to register metadata derived from routing declarations, code patterns, and comments.
General OpenAPI information — such as the API title, version, servers, security schemes, and detailed schemas — is supplied at runtime when the specification is generated.
To configure the compiler plugin extension, use the openApi {} block inside the ktor extension in your build.gradle.kts file:
Configuration options
enabledEnables or disables OpenAPI route annotation code generation. Defaults to
false.codeInferenceEnabledControls whether the compiler attempts to infer OpenAPI metadata from routing code. Defaults to
true. Disable this option if inference produces incorrect results, or you prefer to define metadata explicitly using annotations. For more details, see code inference rules.onlyCommentedLimits metadata generation to routes that contain comment annotations. Defaults to
false, meaning all routing calls are processed except those explicitly marked with@ignore.
Routing structure analysis
The Ktor compiler plugin analyzes your server routing DSL to determine the structural shape of your API. This analysis is based solely on route declarations and does not inspect the contents of route handlers.
The following is automatically inferred from the selectors in the routing API tree:
Merged paths (for example,
/api/v1/users/{id}).HTTP methods (such as
GETandPOST).Path parameters.
Because request parameters, bodies, and responses are handled inside route lambdas, the compiler cannot infer a complete OpenAPI description from the routing structure alone. To enrich the generated metadata, Ktor supports annotations and automatic inference based on common request-handling patterns.
Code inference
When code inference is enabled, the compiler plugin recognizes common Ktor usage patterns and generates equivalent runtime annotations automatically.
The following table summarizes the supported inference rules:
Rule | Description | Input | Output (from annotate scope) |
|---|---|---|---|
Request Body | Provides request body schema from |
|
|
Response Body | Provides response body schema from |
|
|
Response Headers | Includes custom headers for responses |
|
|
Path Parameters | Finds path parameter references |
|
|
Query Parameters | Finds query parameter references |
|
|
Request Headers | Finds request header references |
|
|
Resource API routes | Infers call structure for the Resources routing API |
|
|
Inference follows extracted functions where possible and attempts to generate consistent documentation for typical request and response flows.
Disable inference for an endpoint
If inference produces incorrect metadata for a specific endpoint, you can exclude it by adding an ignore marker:
Annotate routes
To enrich the specification, Ktor supports two ways of annotating routes:
Comment-based annotations, analyzed by the compiler plugin.
Runtime route annotations, defined using the
.describe {}DSL.
You can use either approach or combine both.
Comment-based route annotations
Comment-based annotations provide metadata that cannot be inferred from code and integrate seamlessly with existing routes.
Metadata is defined by placing a keyword at the start of a line, followed by a colon (:) and its value.
You can attach comments directly to route declarations:
Formatting rules
Keywords must appear at the start of the line.
A colon (
:) separates the keyword from its value.Plural forms (for example,
Tags,Responses) allow grouped definitions.Singular forms (for example,
Tag,Response) are also supported.Top-level bullet points (
-) are optional and only affect formatting.
The following variants are equivalent:
Supported comment fields
Tag | Format | Description |
|---|---|---|
|
| Groups the endpoint under a tag |
|
| Path parameter |
|
| Query parameter |
|
| Header parameter |
|
| Cookie parameter |
|
| Request body |
|
| Response definition |
|
| Marks the endpoint as deprecated |
|
| Extended description |
|
| Security requirements |
|
| External documentation link |
Runtime route annotations
In cases where compile-time analysis is insufficient, such as when using dynamic routing, interceptors, or conditional logic, you can attach OpenAPI operation metadata directly to a route at runtime using the .describe {} extension function.
Each annotated route defines an OpenAPI Operation object, which represents a single HTTP operation (for example, GET /users) in the generated OpenAPI specification. The metadata is attached to the routing tree at runtime and is consumed by the OpenAPI and Swagger UI plugins.
The .describe {} DSL maps directly to the OpenAPI specification. Property names and structure correspond to the fields defined for an Operation object, including parameters, request bodies, responses, security requirements, servers, callbacks, and specification extensions (x-*).
The runtime route annotations API is experimental and requires opting in using @OptIn(ExperimentalKtorApi::class):
Runtime annotations are merged with compiler-generated and comment-based metadata. When the same OpenAPI field is defined by multiple sources, values provided by runtime annotations take precedence.
Hide routes from the OpenAPI specification
To exclude a route and its children from the generated OpenAPI document, use the Route.hide() function:
This is useful for internal, administrative, or diagnostic endpoints that should not be published, including routes used to generate the OpenAPI specification itself.
The OpenAPI and Swagger UI plugins call .hide() automatically, so their routes are excluded from the resulting document.
Generate and serve the specification
The OpenAPI specification is assembled at runtime from runtime route annotations and metadata generated by the compiler plugin.
You can expose the specification in the following ways:
Assemble and serve the specification
To assemble a complete OpenAPI document at runtime, create an OpenApiDoc instance and provide the routes that should be included in the specification.
The document is assembled from compiler-generated metadata and runtime route annotations from the routing tree. The resulting OpenApiDoc instance always reflects the current state of the application.
You typically construct the document from a route handler and respond with it directly:
In this example, the OpenAPI document is serialized using the ContentNegotiation plugin. This assumes that a JSON serializer (for example, kotlinx.serialization) is installed.
No additional build or generation step is required. Changes to routes or annotations are reflected automatically the next time the specification is requested.
Serve interactive documentation
To expose the OpenAPI specification through an interactive UI, use the OpenAPI and Swagger UI plugins.
Both plugins assemble the specification at runtime and can read metadata directly from the routing tree. They differ in how the documentation is rendered:
The OpenAPI plugin renders documentation on the server and serves pre-generated HTML.
The Swagger UI plugin serves the OpenAPI specification as JSON or YAML and renders the UI in the browser using Swagger UI.
Metadata precedence
The final OpenAPI specification is assembled at runtime by merging metadata contributed from multiple sources.
The following sources are applied, in order:
Compiler-generated metadata, including:
When the same OpenAPI field is defined by multiple sources, values provided by runtime annotations take precedence over comment-based annotations and compiler-generated metadata.
Metadata that is not explicitly overridden is preserved and merged into the final document.