Base URL: http://localhost:8081
All endpoints require JWT authentication via Custom Identity Service.
Required Header:
Authorization: Bearer <access_token>
Retrieve a paginated list of stalls with optional filtering.
Endpoint: GET /api/stalls
Headers:
Authorization: Bearer <access_token>
Content-Type: application/json
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
status |
string | No | Filter by status. Values: AVAILABLE, HELD, RESERVED |
stallSize |
string | No | Filter by size. Values: SMALL, MEDIUM, LARGE |
location |
string | No | Filter by location (partial match, case-insensitive) |
page |
integer | No | Page number (0-indexed). Default: 0 |
size |
integer | No | Page size. Default: 20 |
sort |
string | No | Sort field and direction. Default: id,asc. Example: code,desc |
Response: 200 OK
{
"content": [
{
"id": 1,
"code": "A-001",
"size": "MEDIUM",
"location": "Hall A, Section 1",
"price": 850.00,
"status": "AVAILABLE",
"createdAt": "2024-01-15T10:30:00",
"updatedAt": "2024-01-15T10:30:00"
},
{
"id": 2,
"code": "A-002",
"size": "LARGE",
"location": "Hall A, Section 1",
"price": 1200.00,
"status": "HELD",
"createdAt": "2024-01-15T10:31:00",
"updatedAt": "2024-01-16T14:20:00"
}
],
"pageable": {
"pageNumber": 0,
"pageSize": 20,
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"offset": 0,
"paged": true,
"unpaged": false
},
"totalElements": 39,
"totalPages": 2,
"last": false,
"size": 20,
"number": 0,
"sort": {
"sorted": true,
"unsorted": false,
"empty": false
},
"numberOfElements": 20,
"first": true,
"empty": false
}Response: 401 Unauthorized
{
"error": "Unauthorized",
"message": "Full authentication is required to access this resource",
"path": "/api/stalls",
"status": 401,
"timestamp": "2024-01-16T15:30:00.123456"
}Retrieve a specific stall by its ID.
Endpoint: GET /api/stalls/{id}
Headers:
Authorization: Bearer <access_token>
Content-Type: application/json
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
integer | Yes | The unique identifier of the stall |
Response: 200 OK
{
"id": 5,
"code": "A-005",
"size": "SMALL",
"location": "Hall A, Section 2",
"price": 650.00,
"status": "AVAILABLE",
"createdAt": "2024-01-15T10:34:00",
"updatedAt": "2024-01-15T10:34:00"
}Response: 404 Not Found
{
"status": 404,
"message": "Stall not found with id: 999",
"timestamp": "2024-01-16T15:30:00.123456"
}Response: 401 Unauthorized
{
"error": "Unauthorized",
"message": "Full authentication is required to access this resource",
"path": "/api/stalls/5",
"status": 401,
"timestamp": "2024-01-16T15:30:00.123456"
}Retrieve a specific stall by its unique code.
Endpoint: GET /api/stalls/code/{code}
Headers:
Authorization: Bearer <access_token>
Content-Type: application/json
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
code |
string | Yes | The unique code of the stall |
Response: 200 OK
{
"id": 12,
"code": "B-003",
"size": "LARGE",
"location": "Hall B, Section 2",
"price": 1150.00,
"status": "RESERVED",
"createdAt": "2024-01-15T10:41:00",
"updatedAt": "2024-01-16T11:20:00"
}Response: 404 Not Found
{
"status": 404,
"message": "Stall not found with code: X-999",
"timestamp": "2024-01-16T15:30:00.123456"
}Response: 401 Unauthorized
{
"error": "Unauthorized",
"message": "Full authentication is required to access this resource",
"path": "/api/stalls/code/B-003",
"status": 401,
"timestamp": "2024-01-16T15:30:00.123456"
}Create a new stall in the system.
Endpoint: POST /api/stalls
Headers:
Authorization: Bearer <access_token>
Content-Type: application/json
Request Body:
{
"code": "D-001",
"size": "MEDIUM",
"location": "Hall D, Section 1",
"price": 900.00
}Request Body Schema:
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
code |
string | Yes | Max 50 chars, not blank | Unique stall code |
size |
string | Yes | Enum: SMALL, MEDIUM, LARGE |
Stall size |
location |
string | Yes | Max 255 chars, not blank | Stall location |
price |
number | Yes | > 0, max 8 integer digits, 2 decimal places | Rental price |
Response: 201 Created
{
"id": 40,
"code": "D-001",
"size": "MEDIUM",
"location": "Hall D, Section 1",
"price": 900.00,
"status": "AVAILABLE",
"createdAt": "2024-01-16T15:30:00",
"updatedAt": "2024-01-16T15:30:00"
}Response: 400 Bad Request (Validation Error)
{
"status": 400,
"message": "Validation failed",
"errors": {
"code": "Code is required",
"size": "Size is required",
"price": "Price must be greater than 0"
},
"timestamp": "2024-01-16T15:30:00.123456"
}Response: 409 Conflict
{
"status": 409,
"message": "Stall with code D-001 already exists",
"timestamp": "2024-01-16T15:30:00.123456"
}Response: 401 Unauthorized
{
"error": "Unauthorized",
"message": "Full authentication is required to access this resource",
"path": "/api/stalls",
"status": 401,
"timestamp": "2024-01-16T15:30:00.123456"
}Update an existing stall's details.
Endpoint: PUT /api/stalls/{id}
Headers:
Authorization: Bearer <access_token>
Content-Type: application/json
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
integer | Yes | The unique identifier of the stall |
Request Body:
{
"code": "A-001-UPDATED",
"size": "LARGE",
"location": "Hall A, Section 1 - Prime Location",
"price": 1500.00
}Request Body Schema:
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
code |
string | No | Max 50 chars | Unique stall code |
size |
string | No | Enum: SMALL, MEDIUM, LARGE |
Stall size |
location |
string | No | Max 255 chars | Stall location |
price |
number | No | > 0, max 8 integer digits, 2 decimal places | Rental price |
Note: All fields are optional. Only provided fields will be updated.
Response: 200 OK
{
"id": 1,
"code": "A-001-UPDATED",
"size": "LARGE",
"location": "Hall A, Section 1 - Prime Location",
"price": 1500.00,
"status": "AVAILABLE",
"createdAt": "2024-01-15T10:30:00",
"updatedAt": "2024-01-16T15:35:00"
}Response: 404 Not Found
{
"status": 404,
"message": "Stall not found with id: 999",
"timestamp": "2024-01-16T15:35:00.123456"
}Response: 409 Conflict
{
"status": 409,
"message": "Stall with code A-002 already exists",
"timestamp": "2024-01-16T15:35:00.123456"
}Response: 400 Bad Request (Validation Error)
{
"status": 400,
"message": "Validation failed",
"errors": {
"price": "Price must be greater than 0",
"code": "Code must not exceed 50 characters"
},
"timestamp": "2024-01-16T15:35:00.123456"
}Response: 401 Unauthorized
{
"error": "Unauthorized",
"message": "Full authentication is required to access this resource",
"path": "/api/stalls/1",
"status": 401,
"timestamp": "2024-01-16T15:35:00.123456"
}Hold an available stall (idempotent operation).
Endpoint: POST /api/stalls/{id}/hold
Headers:
Authorization: Bearer <access_token>
Content-Type: application/json
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
integer | Yes | The unique identifier of the stall |
Business Rules:
- Only
AVAILABLEstalls can be held - If stall is already
HELD, returns current state (idempotent) - Cannot hold
RESERVEDstalls
Response: 200 OK
{
"id": 7,
"code": "A-007",
"size": "MEDIUM",
"location": "Hall A, Section 2",
"price": 850.00,
"status": "HELD",
"createdAt": "2024-01-15T10:36:00",
"updatedAt": "2024-01-16T15:40:00"
}Response: 200 OK (Already Held - Idempotent)
{
"id": 7,
"code": "A-007",
"size": "MEDIUM",
"location": "Hall A, Section 2",
"price": 850.00,
"status": "HELD",
"createdAt": "2024-01-15T10:36:00",
"updatedAt": "2024-01-16T15:40:00"
}Response: 400 Bad Request
{
"status": 400,
"message": "Cannot hold stall with status: RESERVED. Only AVAILABLE stalls can be held.",
"timestamp": "2024-01-16T15:40:00.123456"
}Response: 404 Not Found
{
"status": 404,
"message": "Stall not found with id: 999",
"timestamp": "2024-01-16T15:40:00.123456"
}Response: 401 Unauthorized
{
"error": "Unauthorized",
"message": "Full authentication is required to access this resource",
"path": "/api/stalls/7/hold",
"status": 401,
"timestamp": "2024-01-16T15:40:00.123456"
}Release a held or reserved stall back to available status (idempotent operation).
Endpoint: POST /api/stalls/{id}/release
Headers:
Authorization: Bearer <access_token>
Content-Type: application/json
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
integer | Yes | The unique identifier of the stall |
Business Rules:
- Only
HELDorRESERVEDstalls can be released - If stall is already
AVAILABLE, returns current state (idempotent) - Publishes Kafka event
stall.releasedto topic
Response: 200 OK
{
"id": 8,
"code": "A-008",
"size": "SMALL",
"location": "Hall A, Section 3",
"price": 650.00,
"status": "AVAILABLE",
"createdAt": "2024-01-15T10:37:00",
"updatedAt": "2024-01-16T15:45:00"
}Response: 200 OK (Already Available - Idempotent)
{
"id": 8,
"code": "A-008",
"size": "SMALL",
"location": "Hall A, Section 3",
"price": 650.00,
"status": "AVAILABLE",
"createdAt": "2024-01-15T10:37:00",
"updatedAt": "2024-01-16T15:45:00"
}Response: 400 Bad Request
{
"status": 400,
"message": "Cannot release stall with status: AVAILABLE. Only HELD or RESERVED stalls can be released.",
"timestamp": "2024-01-16T15:45:00.123456"
}Response: 404 Not Found
{
"status": 404,
"message": "Stall not found with id: 999",
"timestamp": "2024-01-16T15:45:00.123456"
}Response: 401 Unauthorized
{
"error": "Unauthorized",
"message": "Full authentication is required to access this resource",
"path": "/api/stalls/8/release",
"status": 401,
"timestamp": "2024-01-16T15:45:00.123456"
}Kafka Event Published:
{
"stallId": 8,
"code": "A-008",
"status": "AVAILABLE",
"location": "Hall A, Section 3"
}Topic: stall.released
Reserve a held stall (idempotent operation).
Endpoint: POST /api/stalls/{id}/reserve
Headers:
Authorization: Bearer <access_token>
Content-Type: application/json
Path Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
integer | Yes | The unique identifier of the stall |
Business Rules:
- Only
HELDstalls can be reserved - If stall is already
RESERVED, returns current state (idempotent) - Cannot reserve
AVAILABLEstalls (must be held first) - Publishes Kafka event
stall.reservedto topic
Response: 200 OK
{
"id": 10,
"code": "A-010",
"size": "LARGE",
"location": "Hall A, Section 3",
"price": 1200.00,
"status": "RESERVED",
"createdAt": "2024-01-15T10:39:00",
"updatedAt": "2024-01-16T15:50:00"
}Response: 200 OK (Already Reserved - Idempotent)
{
"id": 10,
"code": "A-010",
"size": "LARGE",
"location": "Hall A, Section 3",
"price": 1200.00,
"status": "RESERVED",
"createdAt": "2024-01-15T10:39:00",
"updatedAt": "2024-01-16T15:50:00"
}Response: 400 Bad Request
{
"status": 400,
"message": "Cannot reserve stall with status: AVAILABLE. Only HELD stalls can be reserved.",
"timestamp": "2024-01-16T15:50:00.123456"
}Response: 404 Not Found
{
"status": 404,
"message": "Stall not found with id: 999",
"timestamp": "2024-01-16T15:50:00.123456"
}Response: 401 Unauthorized
{
"error": "Unauthorized",
"message": "Full authentication is required to access this resource",
"path": "/api/stalls/10/reserve",
"status": 401,
"timestamp": "2024-01-16T15:50:00.123456"
}Kafka Event Published:
{
"stallId": 10,
"code": "A-010",
"status": "RESERVED",
"location": "Hall A, Section 3"
}Topic: stall.reserved
{
"status": 500,
"message": "An unexpected error occurred: <error details>",
"timestamp": "2024-01-16T15:55:00.123456"
}SMALLMEDIUMLARGE
AVAILABLE- Stall is available for bookingHELD- Stall is temporarily held (can be released or reserved)RESERVED- Stall is reserved (can only be released)
AVAILABLE → HELD → RESERVED → AVAILABLE
↑ ↓ ↓
└─────────┴────────────────────┘
Valid Transitions:
AVAILABLE→HELD(via/hold)HELD→RESERVED(via/reserve)HELD→AVAILABLE(via/release)RESERVED→AVAILABLE(via/release)
Invalid Transitions:
AVAILABLE→RESERVED(must hold first)RESERVED→HELD(must release first)
All paginated endpoints return the following structure:
| Field | Type | Description |
|---|---|---|
content |
array | Array of stall objects |
pageable |
object | Pagination metadata |
totalElements |
integer | Total number of items across all pages |
totalPages |
integer | Total number of pages |
last |
boolean | Whether this is the last page |
size |
integer | Page size |
number |
integer | Current page number (0-indexed) |
sort |
object | Sort information |
numberOfElements |
integer | Number of elements in current page |
first |
boolean | Whether this is the first page |
empty |
boolean | Whether the page is empty |
To obtain an access token, make a POST request to the Identity Service:
Endpoint: POST https://j2bxq20h-8081.asse.devtunnels.ms/api/v1/auth/login
Headers:
Content-Type: application/json
Request Body (Admin User):
{
"username": "admin",
"password": "admin123"
}Response:
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"tokenType": "Bearer",
"expiresIn": 3600
}Test User:
- Admin:
admin/admin123
Refresh Token Endpoint: POST https://j2bxq20h-8081.asse.devtunnels.ms/api/v1/auth/refresh
Refresh Request Body:
{
"refreshToken": "<your_refresh_token>"
}-
Search for available stalls:
GET /api/stalls?status=AVAILABLE&stallSize=MEDIUM -
Hold the stall:
POST /api/stalls/7/hold -
Reserve the stall:
POST /api/stalls/7/reserve
-
Check stall status:
GET /api/stalls/10 -
Release the stall:
POST /api/stalls/10/release
-
Get current stall:
GET /api/stalls/code/A-005 -
Update price and location:
PUT /api/stalls/5 { "price": 750.00, "location": "Hall A, Section 2 - Updated" }
GET /api/stalls?location=hall%20b&page=0&size=10&sort=price,asc