{"openapi":"3.0.3","info":{"title":"TicketTrail API","version":"1.0.0","description":"REST API behind the TicketTrail event-booking practice app.\n\nLog in, then click **Authorize** and paste the token. Every account is an isolated sandbox: featured events are shared and read-only, your own events and bookings are private. Limits: 6 custom events and 9 bookings per account (the oldest is replaced). Send `X-Chaos-Mode: 1` for random latency."},"servers":[{"url":"https://www.qapracticelabs.com/api/tickets"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Auth","description":"Register, log in and check a token"},{"name":"Events","description":"Browse featured events and manage your own"},{"name":"Bookings","description":"Book, view and cancel tickets"},{"name":"Meta","description":"Health and limits"}],"paths":{"/auth/register":{"post":{"security":[],"tags":["Auth"],"summary":"Register a new account","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Credentials"}}}},"responses":{"201":{"description":"Account created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResponse"}}}},"400":{"description":"Validation error or email already registered","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Email already registered"}}}}}}}}},"/auth/login":{"post":{"security":[],"tags":["Auth"],"summary":"Log in and receive a bearer token","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Credentials"}}}},"responses":{"200":{"description":"Logged in","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResponse"}}}},"400":{"description":"Wrong password or validation error","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Invalid credentials"}}}}}},"404":{"description":"No account for this email","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"User not found"}}}}}},"429":{"description":"Too many login attempts","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Too many requests"}}}}}}}}},"/auth/me":{"get":{"tags":["Auth"],"summary":"Who am I? (validates the token)","responses":{"200":{"description":"Token is valid","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"user":{"type":"object","properties":{"userId":{"type":"integer","example":42},"email":{"type":"string","example":"student@example.com"}}}}}}}},"401":{"description":"Missing, invalid or expired token","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Unauthorized"}}}}}}}}},"/events":{"get":{"tags":["Events"],"summary":"List events (featured + yours), soonest first","parameters":[{"name":"category","in":"query","schema":{"type":"string","enum":["Conference","Concert","Sports","Workshop","Festival"]},"description":"Filter by category"},{"name":"city","in":"query","schema":{"type":"string"},"description":"Filter by city (case-insensitive exact match)"},{"name":"search","in":"query","schema":{"type":"string"},"description":"Search title, description and venue"},{"name":"page","in":"query","schema":{"type":"integer","default":1,"minimum":1},"description":"Page number"},{"name":"limit","in":"query","schema":{"type":"integer","default":10,"minimum":1,"maximum":100},"description":"Items per page"}],"responses":{"200":{"description":"Paginated list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/Event"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"401":{"description":"Missing, invalid or expired token","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Unauthorized"}}}}}}}},"post":{"tags":["Events"],"summary":"Create an event","description":"availableSeats starts equal to totalSeats. When you already have 6 events, your oldest one (and its bookings) is deleted.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventInput"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/Event"},"message":{"type":"string","example":"Event created successfully"}}}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"401":{"description":"Missing, invalid or expired token","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Unauthorized"}}}}}}}}},"/events/{id}":{"parameters":[{"name":"id","in":"path","required":true,"description":"Event id","schema":{"type":"integer"}}],"get":{"tags":["Events"],"summary":"Get one event","responses":{"200":{"description":"Found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/Event"}}}}}},"404":{"description":"Not found or owned by someone else","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Event with id 99 not found"}}}}}},"401":{"description":"Missing, invalid or expired token","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Unauthorized"}}}}}}}},"put":{"tags":["Events"],"summary":"Update your event","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventInput"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/Event"},"message":{"type":"string","example":"Event updated successfully"}}}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"403":{"description":"Featured events cannot be changed","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Featured events are read-only"}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Event with id 99 not found"}}}}}},"401":{"description":"Missing, invalid or expired token","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Unauthorized"}}}}}}}},"delete":{"tags":["Events"],"summary":"Delete your event and its bookings","responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"403":{"description":"Featured events cannot be deleted","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Featured events are read-only"}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Event with id 99 not found"}}}}}},"401":{"description":"Missing, invalid or expired token","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Unauthorized"}}}}}}}}},"/bookings":{"get":{"tags":["Bookings"],"summary":"List your bookings, newest first","parameters":[{"name":"eventId","in":"query","schema":{"type":"integer"},"description":"Filter by event"},{"name":"status","in":"query","schema":{"type":"string","enum":["confirmed","cancelled"]},"description":"Filter by status"},{"name":"page","in":"query","schema":{"type":"integer","default":1,"minimum":1},"description":"Page number"},{"name":"limit","in":"query","schema":{"type":"integer","default":10,"minimum":1,"maximum":100},"description":"Items per page"}],"responses":{"200":{"description":"Paginated list","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"type":"array","items":{"$ref":"#/components/schemas/Booking"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"400":{"description":"Validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"401":{"description":"Missing, invalid or expired token","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Unauthorized"}}}}}}}},"post":{"tags":["Bookings"],"summary":"Book tickets","description":"Checks the event exists, checks seats, computes the total and creates a TKT-XXXXXX reference, all in one transaction.\nWhen you already have 9 bookings, the oldest is replaced.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingInput"}}}},"responses":{"201":{"description":"Booked","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/Booking"},"message":{"type":"string","example":"Booking confirmed!"}}}}}},"400":{"description":"Validation error or not enough seats","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Only 3 seat(s) available"}}}}}},"404":{"description":"Event not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Event with id 99 not found"}}}}}},"401":{"description":"Missing, invalid or expired token","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Unauthorized"}}}}}}}},"delete":{"tags":["Bookings"],"summary":"Clear all your bookings (handy before a test run)","responses":{"200":{"description":"Cleared","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string","example":"3 booking(s) cleared"},"deleted":{"type":"integer","example":3}}}}}},"401":{"description":"Missing, invalid or expired token","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Unauthorized"}}}}}}}}},"/bookings/ref/{ref}":{"parameters":[{"name":"ref","in":"path","required":true,"description":"Booking reference, e.g. TKT-7Q2M9X","schema":{"type":"string"}}],"get":{"tags":["Bookings"],"summary":"Find a booking by reference","responses":{"200":{"description":"Found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/Booking"}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Booking with reference \"TKT-XYZ123\" not found"}}}}}},"401":{"description":"Missing, invalid or expired token","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Unauthorized"}}}}}}}}},"/bookings/{id}":{"parameters":[{"name":"id","in":"path","required":true,"description":"Booking id","schema":{"type":"integer"}}],"get":{"tags":["Bookings"],"summary":"Get one booking","responses":{"200":{"description":"Found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":true},"data":{"$ref":"#/components/schemas/Booking"}}}}}},"404":{"description":"Not found or someone else's","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Booking with id 99 not found"}}}}}},"401":{"description":"Missing, invalid or expired token","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Unauthorized"}}}}}}}},"delete":{"tags":["Bookings"],"summary":"Cancel a booking and release its seats","responses":{"200":{"description":"Cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Booking with id 99 not found"}}}}}},"401":{"description":"Missing, invalid or expired token","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Unauthorized"}}}}}}}}},"/health":{"get":{"security":[],"tags":["Meta"],"summary":"API and database health","responses":{"200":{"description":"Healthy","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"timestamp":{"type":"string","format":"date-time"},"dbStatus":{"type":"string","example":"connected"}}}}}}}}},"/config":{"get":{"security":[],"tags":["Meta"],"summary":"Sandbox limits and categories","responses":{"200":{"description":"Limits","content":{"application/json":{"schema":{"type":"object","properties":{"maxCustomEvents":{"type":"integer","example":6},"maxBookings":{"type":"integer","example":9},"maxTicketsPerBooking":{"type":"integer","example":10},"categories":{"type":"array","items":{"type":"string"}}}}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer"}},"schemas":{"Credentials":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email","example":"demo@practice.test"},"password":{"type":"string","minLength":6,"example":"Demo@1234"}}},"AuthResponse":{"type":"object","properties":{"success":{"type":"boolean","example":true},"token":{"type":"string","example":"12|Qm9vay\u2026"},"user":{"type":"object","properties":{"id":{"type":"integer","example":42},"email":{"type":"string","example":"student@example.com"}}}}},"Event":{"type":"object","properties":{"id":{"type":"integer","example":1},"title":{"type":"string","example":"CloudScale Engineering Summit"},"description":{"type":"string","nullable":true},"category":{"type":"string","enum":["Conference","Concert","Sports","Workshop","Festival"]},"venue":{"type":"string","example":"Agargaon Convention Hall"},"city":{"type":"string","example":"Dhaka"},"eventDate":{"type":"string","format":"date-time"},"price":{"type":"number","example":1200,"description":"Ticket price in Bangladeshi Taka (BDT); 0 means free"},"totalSeats":{"type":"integer","example":500},"availableSeats":{"type":"integer","example":497},"imageUrl":{"type":"string","nullable":true},"isStatic":{"type":"boolean","description":"true for featured, read-only events"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"EventInput":{"type":"object","required":["title","category","venue","city","eventDate","price","totalSeats"],"properties":{"title":{"type":"string","minLength":3,"example":"Selenium Grid Bootcamp"},"description":{"type":"string","example":"Hands-on day of parallel browser testing."},"category":{"type":"string","enum":["Conference","Concert","Sports","Workshop","Festival"],"example":"Workshop"},"venue":{"type":"string","example":"Sylhet Hi-Tech Park, Hall B"},"city":{"type":"string","example":"Sylhet"},"eventDate":{"type":"string","format":"date-time","example":"2027-01-29T14:00:00.000000Z","description":"Must be in the future"},"price":{"type":"number","minimum":0,"example":799,"description":"Ticket price in BDT"},"totalSeats":{"type":"integer","minimum":1,"example":50},"imageUrl":{"type":"string","format":"uri","nullable":true}}},"Booking":{"type":"object","properties":{"id":{"type":"integer","example":7},"eventId":{"type":"integer","example":1},"customerName":{"type":"string","example":"Nusrat Jahan"},"customerEmail":{"type":"string","example":"nusrat@example.com"},"customerPhone":{"type":"string","example":"+880 1712-345678"},"quantity":{"type":"integer","example":2},"totalPrice":{"type":"number","example":2400,"description":"Total in BDT"},"status":{"type":"string","enum":["confirmed","cancelled"]},"bookingRef":{"type":"string","example":"TKT-7Q2M9X"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"event":{"$ref":"#/components/schemas/Event"}}},"BookingInput":{"type":"object","required":["eventId","customerName","customerEmail","customerPhone","quantity"],"properties":{"eventId":{"type":"integer","example":1},"customerName":{"type":"string","minLength":2,"example":"Nusrat Jahan"},"customerEmail":{"type":"string","format":"email","example":"nusrat@example.com"},"customerPhone":{"type":"string","minLength":10,"example":"+880 1712-345678"},"quantity":{"type":"integer","minimum":1,"maximum":10,"example":2}}},"Pagination":{"type":"object","properties":{"total":{"type":"integer","example":14},"page":{"type":"integer","example":1},"limit":{"type":"integer","example":10},"totalPages":{"type":"integer","example":2}}},"Message":{"type":"object","properties":{"success":{"type":"boolean","example":true},"message":{"type":"string"}}},"ValidationError":{"type":"object","properties":{"success":{"type":"boolean","example":false},"error":{"type":"string","example":"Validation failed"},"details":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string","example":"customerEmail"},"message":{"type":"string","example":"A valid email is required"}}}}}}}}}