Swagger Core annotations library providing OpenAPI 3.x annotations for Java applications, enabling developers to document REST APIs with metadata annotations
—
This document covers annotations for documenting API responses, including status codes, headers, content types, and comprehensive response scenarios.
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.headers.Header;
import io.swagger.v3.oas.annotations.links.Link;
import io.swagger.v3.oas.annotations.links.LinkParameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.media.ExampleObject;Defines comprehensive operation response documentation including status codes, descriptions, headers, content types, and links to related operations.
@GetMapping("/pets/{id}")
@Operation(summary = "Get pet by ID", tags = {"pets"})
@ApiResponse(
responseCode = "200",
description = "Successful operation - Pet found and returned",
headers = {
@Header(
name = "X-Rate-Limit-Remaining",
description = "Number of requests remaining in current time window",
schema = @Schema(type = "integer", minimum = "0")
),
@Header(
name = "X-Rate-Limit-Reset",
description = "Time when rate limit resets (Unix timestamp)",
schema = @Schema(type = "integer", format = "int64")
)
},
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = Pet.class),
examples = {
@ExampleObject(
name = "successfulPetResponse",
summary = "Successful pet retrieval",
description = "Example of a successfully retrieved pet",
value = """
{
"id": 123,
"name": "Fluffy",
"category": {
"id": 1,
"name": "Dogs"
},
"status": "available",
"tags": ["friendly", "trained"],
"photoUrls": ["https://example.com/photos/fluffy.jpg"]
}
"""
)
}
)
)
@ApiResponse(
responseCode = "400",
description = "Invalid pet ID supplied - ID must be a positive integer",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ErrorResponse.class),
examples = @ExampleObject(
name = "invalidIdError",
summary = "Invalid ID error",
value = """
{
"error": "INVALID_PARAMETER",
"message": "Pet ID must be a positive integer",
"field": "id",
"code": 400
}
"""
)
)
)
@ApiResponse(
responseCode = "404",
description = "Pet not found - No pet exists with the provided ID",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ErrorResponse.class),
examples = @ExampleObject(
name = "petNotFoundError",
summary = "Pet not found error",
value = """
{
"error": "RESOURCE_NOT_FOUND",
"message": "No pet found with ID 123",
"resourceType": "Pet",
"resourceId": "123",
"code": 404
}
"""
)
)
)
public ResponseEntity<Pet> getPetById(@PathVariable Long id) {
Pet pet = petService.findById(id);
return ResponseEntity.ok()
.header("X-Rate-Limit-Remaining", "99")
.header("X-Rate-Limit-Reset", String.valueOf(System.currentTimeMillis() + 3600000))
.body(pet);
}@GetMapping(value = "/pets/{id}", produces = {"application/json", "application/xml", "text/plain"})
@ApiResponse(
responseCode = "200",
description = "Pet information in requested format",
content = {
@Content(
mediaType = "application/json",
schema = @Schema(implementation = Pet.class),
examples = @ExampleObject(
name = "jsonPet",
value = "{ \"id\": 1, \"name\": \"Fluffy\", \"status\": \"available\" }"
)
),
@Content(
mediaType = "application/xml",
schema = @Schema(implementation = Pet.class),
examples = @ExampleObject(
name = "xmlPet",
value = "<pet><id>1</id><name>Fluffy</name><status>available</status></pet>"
)
),
@Content(
mediaType = "text/plain",
schema = @Schema(type = "string"),
examples = @ExampleObject(
name = "textPet",
value = "Pet ID: 1, Name: Fluffy, Status: available"
)
)
}
)@PostMapping("/pets")
@ApiResponse(
responseCode = "201",
description = "Pet created successfully",
headers = @Header(
name = "Location",
description = "URL of the newly created pet resource",
schema = @Schema(type = "string", format = "uri")
),
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = Pet.class)
),
links = {
@Link(
name = "GetPetById",
description = "Link to retrieve the created pet",
operationId = "getPetById",
parameters = @LinkParameter(
name = "id",
expression = "$response.body#/id"
)
),
@Link(
name = "UpdatePet",
description = "Link to update the created pet",
operationId = "updatePet",
parameters = @LinkParameter(
name = "id",
expression = "$response.body#/id"
)
),
@Link(
name = "DeletePet",
description = "Link to delete the created pet",
operationId = "deletePet",
parameters = @LinkParameter(
name = "id",
expression = "$response.body#/id"
)
)
}
)@PutMapping("/pets/{id}")
@ApiResponse(
responseCode = "200",
description = "Pet updated successfully",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = Pet.class)
)
)
@ApiResponse(
responseCode = "201",
description = "Pet created (ID did not exist)",
headers = @Header(
name = "Location",
description = "URL of the newly created pet",
schema = @Schema(type = "string", format = "uri")
),
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = Pet.class)
)
)
@ApiResponse(
responseCode = "400",
description = "Invalid request data",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ValidationErrorResponse.class)
)
)@GetMapping("/pets")
@ApiResponse(
responseCode = "200",
description = "List of pets retrieved successfully",
headers = {
@Header(
name = "X-Total-Count",
description = "Total number of pets available",
schema = @Schema(type = "integer", minimum = "0"),
example = "150"
),
@Header(
name = "X-Page-Count",
description = "Total number of pages available",
schema = @Schema(type = "integer", minimum = "1"),
example = "8"
),
@Header(
name = "X-Per-Page",
description = "Number of items per page",
schema = @Schema(type = "integer", minimum = "1", maximum = "100"),
example = "20"
),
@Header(
name = "X-Current-Page",
description = "Current page number",
schema = @Schema(type = "integer", minimum = "1"),
example = "1"
),
@Header(
name = "Link",
description = "Pagination links (RFC 5988)",
schema = @Schema(type = "string"),
example = "<https://api.petstore.io/pets?page=2>; rel=\"next\", <https://api.petstore.io/pets?page=8>; rel=\"last\""
)
},
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = PetListResponse.class)
)
)public @interface ApiResponse {
String description() default "";
String responseCode() default "default";
Header[] headers() default {};
Link[] links() default {};
Content[] content() default {};
Extension[] extensions() default {};
String ref() default "";
boolean useReturnTypeSchema() default false;
}Container annotation for multiple response scenarios, allowing comprehensive documentation of all possible operation outcomes.
@PostMapping("/pets")
@Operation(summary = "Create a new pet", tags = {"pets"})
@ApiResponses({
@ApiResponse(
responseCode = "201",
description = "Pet created successfully",
headers = @Header(
name = "Location",
description = "URI of the created pet resource",
schema = @Schema(type = "string", format = "uri")
),
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = Pet.class),
examples = @ExampleObject(
name = "createdPet",
summary = "Successfully created pet",
value = """
{
"id": 124,
"name": "New Pet",
"category": {"id": 1, "name": "Dogs"},
"status": "available"
}
"""
)
),
links = @Link(
name = "GetCreatedPet",
operationId = "getPetById",
parameters = @LinkParameter(name = "id", expression = "$response.body#/id")
)
),
@ApiResponse(
responseCode = "400",
description = "Invalid input data provided",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ValidationErrorResponse.class),
examples = {
@ExampleObject(
name = "missingRequiredFields",
summary = "Missing required fields",
description = "Error when required fields are not provided",
value = """
{
"error": "VALIDATION_ERROR",
"message": "Required fields are missing",
"violations": [
{
"field": "name",
"message": "Pet name is required"
},
{
"field": "status",
"message": "Pet status must be specified"
}
]
}
"""
),
@ExampleObject(
name = "invalidFieldValues",
summary = "Invalid field values",
description = "Error when field values don't meet constraints",
value = """
{
"error": "VALIDATION_ERROR",
"message": "Invalid field values provided",
"violations": [
{
"field": "name",
"message": "Pet name must be between 1 and 50 characters"
},
{
"field": "status",
"message": "Status must be one of: available, pending, sold"
}
]
}
"""
)
}
)
),
@ApiResponse(
responseCode = "401",
description = "Authentication required - Invalid or missing API key",
headers = @Header(
name = "WWW-Authenticate",
description = "Authentication method required",
schema = @Schema(type = "string", defaultValue = "ApiKey")
),
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ErrorResponse.class),
examples = @ExampleObject(
name = "authenticationError",
value = """
{
"error": "AUTHENTICATION_REQUIRED",
"message": "Valid API key is required",
"code": 401
}
"""
)
)
),
@ApiResponse(
responseCode = "403",
description = "Insufficient permissions - User cannot create pets",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ErrorResponse.class),
examples = @ExampleObject(
name = "permissionError",
value = """
{
"error": "INSUFFICIENT_PERMISSIONS",
"message": "User does not have permission to create pets",
"requiredPermission": "pets:create",
"code": 403
}
"""
)
)
),
@ApiResponse(
responseCode = "409",
description = "Conflict - Pet with same name already exists",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ConflictErrorResponse.class),
examples = @ExampleObject(
name = "conflictError",
value = """
{
"error": "RESOURCE_CONFLICT",
"message": "A pet with this name already exists",
"conflictField": "name",
"conflictValue": "Fluffy",
"existingResourceId": 123,
"code": 409
}
"""
)
)
),
@ApiResponse(
responseCode = "422",
description = "Unprocessable Entity - Request data fails business rules",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = BusinessRuleErrorResponse.class),
examples = @ExampleObject(
name = "businessRuleError",
value = """
{
"error": "BUSINESS_RULE_VIOLATION",
"message": "Cannot create pet: Maximum pet limit reached for this user",
"rule": "MAX_PETS_PER_USER",
"limit": 10,
"current": 10,
"code": 422
}
"""
)
)
),
@ApiResponse(
responseCode = "429",
description = "Too Many Requests - Rate limit exceeded",
headers = {
@Header(
name = "X-Rate-Limit-Limit",
description = "Request limit per time window",
schema = @Schema(type = "integer")
),
@Header(
name = "X-Rate-Limit-Remaining",
description = "Remaining requests in current window",
schema = @Schema(type = "integer")
),
@Header(
name = "X-Rate-Limit-Reset",
description = "Time when rate limit resets",
schema = @Schema(type = "integer", format = "int64")
),
@Header(
name = "Retry-After",
description = "Seconds to wait before retrying",
schema = @Schema(type = "integer")
)
},
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = RateLimitErrorResponse.class),
examples = @ExampleObject(
name = "rateLimitError",
value = """
{
"error": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Rate limit exceeded.",
"retryAfter": 60,
"code": 429
}
"""
)
)
),
@ApiResponse(
responseCode = "500",
description = "Internal Server Error - Unexpected server error occurred",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ErrorResponse.class),
examples = @ExampleObject(
name = "serverError",
value = """
{
"error": "INTERNAL_SERVER_ERROR",
"message": "An unexpected error occurred while processing the request",
"traceId": "abc123def456",
"code": 500
}
"""
)
)
)
})
public ResponseEntity<Pet> createPet(@Valid @RequestBody CreatePetRequest request) {
Pet createdPet = petService.create(request);
URI location = ServletUriComponentsBuilder
.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(createdPet.getId())
.toUri();
return ResponseEntity.created(location).body(createdPet);
}@PostMapping("/pets/{id}/process")
@ApiResponses({
@ApiResponse(
responseCode = "202",
description = "Processing started - Operation accepted for asynchronous processing",
headers = {
@Header(
name = "Location",
description = "URL to check processing status",
schema = @Schema(type = "string", format = "uri")
),
@Header(
name = "X-Process-ID",
description = "Unique identifier for the processing job",
schema = @Schema(type = "string", format = "uuid")
)
},
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ProcessingResponse.class),
examples = @ExampleObject(
name = "processingStarted",
value = """
{
"processId": "123e4567-e89b-12d3-a456-426614174000",
"status": "processing",
"message": "Pet processing has been started",
"estimatedCompletion": "2024-01-15T10:30:00Z",
"statusUrl": "/api/v1/processes/123e4567-e89b-12d3-a456-426614174000"
}
"""
)
),
links = {
@Link(
name = "CheckStatus",
description = "Check processing status",
operationId = "getProcessStatus",
parameters = @LinkParameter(
name = "processId",
expression = "$response.body#/processId"
)
),
@Link(
name = "CancelProcess",
description = "Cancel the processing job",
operationId = "cancelProcess",
parameters = @LinkParameter(
name = "processId",
expression = "$response.body#/processId"
)
)
}
),
@ApiResponse(
responseCode = "303",
description = "See Other - Processing already completed, redirect to result",
headers = @Header(
name = "Location",
description = "URL of the completed processing result",
schema = @Schema(type = "string", format = "uri")
)
)
})@PostMapping("/pets/bulk")
@ApiResponses({
@ApiResponse(
responseCode = "207",
description = "Multi-Status - Partial success with mixed results",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = BulkOperationResponse.class),
examples = @ExampleObject(
name = "partialSuccess",
summary = "Partial success example",
description = "Some pets created successfully, others failed",
value = """
{
"overallStatus": "partial_success",
"totalRequests": 5,
"successCount": 3,
"failureCount": 2,
"results": [
{
"index": 0,
"status": 201,
"success": true,
"data": {"id": 101, "name": "Pet1", "status": "available"}
},
{
"index": 1,
"status": 400,
"success": false,
"error": {"message": "Invalid pet name", "field": "name"}
},
{
"index": 2,
"status": 201,
"success": true,
"data": {"id": 102, "name": "Pet2", "status": "available"}
},
{
"index": 3,
"status": 409,
"success": false,
"error": {"message": "Pet name already exists", "conflictField": "name"}
},
{
"index": 4,
"status": 201,
"success": true,
"data": {"id": 103, "name": "Pet3", "status": "available"}
}
]
}
"""
)
)
),
@ApiResponse(
responseCode = "200",
description = "All operations completed successfully",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = BulkOperationResponse.class)
)
),
@ApiResponse(
responseCode = "400",
description = "All operations failed due to invalid input",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = BulkOperationResponse.class)
)
)
})@GetMapping("/pets/{id}/photo")
@ApiResponses({
@ApiResponse(
responseCode = "200",
description = "Pet photo download",
headers = {
@Header(
name = "Content-Type",
description = "Image media type",
schema = @Schema(type = "string", allowableValues = {"image/jpeg", "image/png", "image/gif"})
),
@Header(
name = "Content-Length",
description = "File size in bytes",
schema = @Schema(type = "integer", minimum = "0")
),
@Header(
name = "Content-Disposition",
description = "File download disposition",
schema = @Schema(type = "string"),
example = "attachment; filename=\"pet-123-photo.jpg\""
),
@Header(
name = "Cache-Control",
description = "Cache control directives",
schema = @Schema(type = "string"),
example = "public, max-age=3600"
),
@Header(
name = "ETag",
description = "Entity tag for caching",
schema = @Schema(type = "string"),
example = "\"abc123def456\""
),
@Header(
name = "Last-Modified",
description = "Last modification date",
schema = @Schema(type = "string", format = "date-time")
)
},
content = {
@Content(mediaType = "image/jpeg", schema = @Schema(type = "string", format = "binary")),
@Content(mediaType = "image/png", schema = @Schema(type = "string", format = "binary")),
@Content(mediaType = "image/gif", schema = @Schema(type = "string", format = "binary"))
}
),
@ApiResponse(
responseCode = "304",
description = "Not Modified - Photo has not changed since last request",
headers = {
@Header(
name = "Cache-Control",
schema = @Schema(type = "string")
),
@Header(
name = "ETag",
schema = @Schema(type = "string")
)
}
),
@ApiResponse(
responseCode = "404",
description = "Photo not found for this pet"
)
})public @interface ApiResponses {
ApiResponse[] value() default {};
}@Schema(description = "Standard error response")
public class ErrorResponse {
@Schema(description = "Error code identifier", example = "RESOURCE_NOT_FOUND")
private String error;
@Schema(description = "Human-readable error message", example = "The requested resource was not found")
private String message;
@Schema(description = "HTTP status code", example = "404")
private Integer code;
@Schema(description = "Request trace ID for debugging", example = "abc123def456")
private String traceId;
@Schema(description = "Timestamp of the error", format = "date-time")
private Instant timestamp;
}
@Schema(description = "Validation error response with field-level details")
public class ValidationErrorResponse extends ErrorResponse {
@Schema(description = "List of field validation violations")
private List<FieldViolation> violations;
@Schema(description = "Field validation violation details")
public static class FieldViolation {
@Schema(description = "Field name that failed validation", example = "name")
private String field;
@Schema(description = "Validation failure message", example = "must not be blank")
private String message;
@Schema(description = "Rejected value", example = "")
private Object rejectedValue;
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-swagger-core-v3--swagger-annotations