diff --git a/pom.xml b/pom.xml
index 9fd3161..d87c284 100644
--- a/pom.xml
+++ b/pom.xml
@@ -136,6 +136,11 @@
org.flywaydb
flyway-database-postgresql
+
+ org.springdoc
+ springdoc-openapi-starter-webmvc-ui
+ 3.0.3
+
diff --git a/src/main/java/com/example/boka/booking/infrastructure/BookingController.java b/src/main/java/com/example/boka/booking/infrastructure/BookingController.java
index e01b22d..291f9ca 100644
--- a/src/main/java/com/example/boka/booking/infrastructure/BookingController.java
+++ b/src/main/java/com/example/boka/booking/infrastructure/BookingController.java
@@ -12,9 +12,12 @@
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
+import io.swagger.v3.oas.annotations.tags.Tag;
+
import java.util.HashMap;
import java.util.Map;
+@Tag(name = "Bookings", description = "Create and manage gym class bookings")
@RestController
@RequestMapping("/api/bookings")
@RequiredArgsConstructor
diff --git a/src/main/java/com/example/boka/config/OpenApiConfig.java b/src/main/java/com/example/boka/config/OpenApiConfig.java
new file mode 100644
index 0000000..e1f7a94
--- /dev/null
+++ b/src/main/java/com/example/boka/config/OpenApiConfig.java
@@ -0,0 +1,15 @@
+package com.example.boka.config;
+
+import io.swagger.v3.oas.annotations.OpenAPIDefinition;
+import io.swagger.v3.oas.annotations.info.Info;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@OpenAPIDefinition(
+ info = @Info(
+ title = "Boka API",
+ description = "Gym class booking platform — REST API",
+ version = "1.0.0"
+ )
+)
+public class OpenApiConfig {}
diff --git a/src/main/java/com/example/boka/gym/infrastructure/GymController.java b/src/main/java/com/example/boka/gym/infrastructure/GymController.java
index 31e6c4d..54cd83c 100644
--- a/src/main/java/com/example/boka/gym/infrastructure/GymController.java
+++ b/src/main/java/com/example/boka/gym/infrastructure/GymController.java
@@ -7,10 +7,12 @@
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
+import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
+@Tag(name = "Gyms", description = "List and locate gyms")
@RestController
@RequestMapping("/api/gyms")
@RequiredArgsConstructor
diff --git a/src/main/java/com/example/boka/gymclass/infrastructure/AdminGymClassController.java b/src/main/java/com/example/boka/gymclass/infrastructure/AdminGymClassController.java
index 760d5d2..5542abc 100644
--- a/src/main/java/com/example/boka/gymclass/infrastructure/AdminGymClassController.java
+++ b/src/main/java/com/example/boka/gymclass/infrastructure/AdminGymClassController.java
@@ -6,6 +6,7 @@
import com.example.boka.gymclass.application.ClassTypeResponse;
import com.example.boka.gymclass.application.CreateGymClassRequest;
import com.example.boka.gymclass.application.UpdateGymClassRequest;
+import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
@@ -17,6 +18,7 @@
import java.util.List;
import java.util.Map;
+@Tag(name = "Admin", description = "Admin-only: manage gym classes, instructors, and class types")
@RestController
@RequestMapping("/api/admin")
@RequiredArgsConstructor
diff --git a/src/main/java/com/example/boka/gymclass/infrastructure/ClassSearchController.java b/src/main/java/com/example/boka/gymclass/infrastructure/ClassSearchController.java
index 5294b82..c4f64ad 100644
--- a/src/main/java/com/example/boka/gymclass/infrastructure/ClassSearchController.java
+++ b/src/main/java/com/example/boka/gymclass/infrastructure/ClassSearchController.java
@@ -10,10 +10,12 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
+import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.user.OAuth2User;
+@Tag(name = "Classes", description = "Search and browse gym classes")
@RestController
@RequestMapping("/api/classes")
@RequiredArgsConstructor
diff --git a/src/main/java/com/example/boka/security/AuthController.java b/src/main/java/com/example/boka/security/AuthController.java
index a90a472..5b4482a 100644
--- a/src/main/java/com/example/boka/security/AuthController.java
+++ b/src/main/java/com/example/boka/security/AuthController.java
@@ -11,12 +11,14 @@
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.user.OAuth2User;
+import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
+@Tag(name = "Auth", description = "Login, logout, registration, and current user")
@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
diff --git a/src/main/java/com/example/boka/security/SecurityConfig.java b/src/main/java/com/example/boka/security/SecurityConfig.java
index 2b74a61..fe1d3fe 100644
--- a/src/main/java/com/example/boka/security/SecurityConfig.java
+++ b/src/main/java/com/example/boka/security/SecurityConfig.java
@@ -58,7 +58,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.cors(Customizer.withDefaults())
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
- .requestMatchers("/", "/index.html", "/search", "/gyms", "/bookings", "/settings", "/admin", "/static/**", "/assets/**", "/api/classes/**", "/api/gyms/**", "/api/auth/me", "/api/auth/register").permitAll()
+ .requestMatchers("/", "/index.html", "/search", "/gyms", "/bookings", "/settings", "/admin", "/static/**", "/assets/**", "/api/classes/**", "/api/gyms/**", "/api/auth/me", "/api/auth/register", "/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
diff --git a/src/main/java/com/example/boka/user/infrastructure/UserController.java b/src/main/java/com/example/boka/user/infrastructure/UserController.java
index 455f6f7..756b70e 100644
--- a/src/main/java/com/example/boka/user/infrastructure/UserController.java
+++ b/src/main/java/com/example/boka/user/infrastructure/UserController.java
@@ -11,11 +11,13 @@
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.validation.BindingResult;
+import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
+@Tag(name = "Users", description = "User profile and account management")
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index da532c5..8a190c4 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -34,3 +34,8 @@ spring.security.oauth2.client.registration.google.scope=profile,email
# Security (temporary - for local testing)
spring.security.user.name=admin
spring.security.user.password=admin
+
+# OpenAPI / Swagger UI
+springdoc.api-docs.path=/v3/api-docs
+springdoc.swagger-ui.path=/swagger-ui.html
+springdoc.swagger-ui.try-it-out-enabled=true