Base URL: /api/v1
All endpoints require:
X-Mock-User-Id: uuid(Phase 1 auth)Idempotency-Key: uuidfor all write operations
→ List available seats with price/position
Hold seats (creates PENDING reservation)
Request headers:
X-Mock-User-Id: uuidIdempotency-Key: a0eebc999c0b4ef8bb6d6bb9bd380a11(UUID v4 lowercase no hyphens)
Request body:
{
"seats": [
{ "seatId": "uuid" },
{ "seatId": "uuid" }
]
}Success (201 Created):
{
"data": {
"reservationId": "uuid",
"expiresAt": "2025-11-22T10:15:00+00:00",
"totalAmount": 15000,
"seatCount": 2
}
}- Must be UUID v4
- Must be lowercase
- Must NOT contain hyphens
- Example valid:
f47ac10b58cf4b1b9c2e6d7f8a91b2c3
The Idempotency-Key header is mandatory for all write operations.
Validation rules:
- Must match the following regex (UUID v4, lowercase, no hyphens):
^[0-9a-f]{8}[0-9a-f]{4}4[0-9a-f]{3}[89ab][0-9a-f]{3}[0-9a-f]{12}$
-
Any missing or invalid
Idempotency-KeyMUST result in:- HTTP status:
422 Unprocessable Entity - Error code:
VALIDATION_ERROR - Response body (RFC 7807-style):
{ "error": { "code": "VALIDATION_ERROR", "message": "Invalid request", "violations": [ { "field": "Idempotency-Key", "message": "Idempotency-Key must be a lowercase UUID v4 without hyphens" } ] } } - HTTP status:
The server MUST NOT start any reservation transaction when the Idempotency-Key header fails validation.
{
"error": {
"code": "SEAT_ALREADY_RESERVED",
"message": "One or more seats are no longer available"
}
}| Code | HTTP | Meaning | Client Action |
|---|---|---|---|
| SEAT_ALREADY_RESERVED | 409 | Seat taken by someone else | Refresh & retry |
| LOCK_TIMEOUT | 503 | DB lock timeout | Auto-retry with backoff |
| TOO_MANY_SEATS | 400 | >5 seats requested | Reduce selection |
| IDEMPOTENCY_KEY_IN_USE | 409 | Request already processing | Wait or new key |
| RESERVATION_EXPIRED | 410 | Hold expired | Start over |
| VALIDATION_ERROR | 422 | RFC 7807 Problem Details | Fix input |
Validation errors use RFC 7807 format with violations[].
If a request with the same Idempotency-Key is already being processed:
- Status: 409 Conflict
- Body:
{
"error": {
"code": "IDEMPOTENCY_KEY_IN_USE",
"message": "Another request with this idempotency key is in progress"
}
}Business rule: maximum 5 seats per reservation.
If exceeded:
- Status: 400 Bad Request
- Code:
TOO_MANY_SEATS
The /hold endpoint uses all-or-nothing semantics.
If any requested seat is no longer available:
- Entire request fails
- Status: 409 Conflict
- Code:
SEAT_ALREADY_RESERVED - Response includes:
{
"error": {
"code": "SEAT_ALREADY_RESERVED",
"message": "One or more seats are no longer available",
"details": {
"unavailable_seat_ids": ["uuid1", "uuid3"]
}
}
}A PENDING reservation expires exactly 900 seconds (15 minutes) after creation.
Field: expiresAt = createdAt + 900 seconds