{"openapi":"3.0.0","info":{"title":"Händigo API","version":"1.0.0","description":"Backend API for Händigo — a Swedish home-services marketplace. Vi fixar. Du njuter."},"servers":[{"url":"http://localhost:4000","description":"Local development server"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"Role":{"type":"string","enum":["admin","employee","customer","partner"]},"BookingStatus":{"type":"string","enum":["confirmed","assigned","enroute","in_progress","completed"]},"InvoiceStatus":{"type":"string","enum":["unpaid","paid"]},"MessageFrom":{"type":"string","enum":["customer","pro"]},"PhotoKind":{"type":"string","enum":["before","after"]},"Frequency":{"type":"string","enum":["once","weekly","biweekly","monthly"]},"ServiceCategory":{"type":"string","enum":["hem","fastighet","foretag"]},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"message":{"type":"string"},"code":{"type":"string"},"details":{"type":"object"}}}}},"User":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"email":{"type":"string","format":"email"},"role":{"$ref":"#/components/schemas/Role"},"phone":{"type":"string","nullable":true},"address":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"createdAt":{"type":"string","format":"date-time"}}},"Service":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"slug":{"type":"string"},"name":{"type":"string"},"category":{"$ref":"#/components/schemas/ServiceCategory"},"icon":{"type":"string"},"tagline":{"type":"string"},"description":{"type":"string"},"priceFrom":{"type":"integer"},"unit":{"type":"string"},"duration":{"type":"string"},"rating":{"type":"number","nullable":true},"reviewCount":{"type":"integer"},"popular":{"type":"boolean"},"rutAvdrag":{"type":"boolean"},"includes":{"type":"array","items":{"type":"string"}},"img":{"type":"string"}}},"Professional":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"title":{"type":"string"},"city":{"type":"string"},"color":{"type":"string"},"rating":{"type":"number","nullable":true},"reviews":{"type":"integer"},"jobs":{"type":"integer"},"years":{"type":"integer"},"verified":{"type":"boolean"},"bio":{"type":"string"},"skills":{"type":"array","items":{"type":"string"}},"langs":{"type":"array","items":{"type":"string"}},"serviceIds":{"type":"array","items":{"type":"string","format":"uuid"}},"createdAt":{"type":"string","format":"date-time"}}},"Booking":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"customerId":{"type":"string","format":"uuid"},"serviceId":{"type":"string","format":"uuid"},"proId":{"type":"string","format":"uuid","nullable":true},"date":{"type":"string","format":"date"},"time":{"type":"string"},"address":{"type":"string"},"frequency":{"$ref":"#/components/schemas/Frequency"},"hours":{"type":"integer"},"notes":{"type":"string"},"price":{"type":"integer"},"status":{"$ref":"#/components/schemas/BookingStatus"},"createdAt":{"type":"string","format":"date-time"}}},"Message":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"bookingId":{"type":"string","format":"uuid"},"sender":{"$ref":"#/components/schemas/MessageFrom"},"text":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}},"Invoice":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"bookingId":{"type":"string","format":"uuid"},"number":{"type":"string"},"amount":{"type":"integer"},"rutRot":{"type":"integer"},"vat":{"type":"integer"},"total":{"type":"integer"},"status":{"$ref":"#/components/schemas/InvoiceStatus"},"issuedAt":{"type":"string","format":"date-time"},"dueAt":{"type":"string","format":"date-time"},"paidAt":{"type":"string","format":"date-time","nullable":true},"method":{"type":"string","nullable":true}}},"Review":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"bookingId":{"type":"string","format":"uuid"},"customerId":{"type":"string","format":"uuid"},"proId":{"type":"string","format":"uuid"},"serviceId":{"type":"string","format":"uuid"},"rating":{"type":"integer","minimum":1,"maximum":5},"text":{"type":"string"},"createdAt":{"type":"string","format":"date-time"}}}}},"security":[],"paths":{"/api/services":{"get":{"summary":"List all services","tags":["Services"],"responses":{"200":{"description":"List of services","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Service"}}}}}}},"post":{"summary":"Create a service (admin)","tags":["Services"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["slug","name","category","icon","priceFrom"],"properties":{"slug":{"type":"string"},"name":{"type":"string"},"category":{"type":"string","enum":["hem","fastighet","foretag"]},"icon":{"type":"string"},"tagline":{"type":"string"},"description":{"type":"string"},"priceFrom":{"type":"integer"},"unit":{"type":"string"},"duration":{"type":"string"},"rating":{"type":"number"},"reviewCount":{"type":"integer"},"popular":{"type":"boolean"},"rutAvdrag":{"type":"boolean"},"includes":{"type":"array","items":{"type":"string"}},"img":{"type":"string"}}}}}},"responses":{"201":{"description":"Service created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Service"}}}}}}},"/api/services/{id}":{"get":{"summary":"Get a single service by id","tags":["Services"],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Service found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Service"}}}},"404":{"description":"Service not found"}}},"put":{"summary":"Update a service (admin)","tags":["Services"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object"}}}},"responses":{"200":{"description":"Service updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Service"}}}}}},"delete":{"summary":"Delete a service (admin)","tags":["Services"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Service deleted"},"404":{"description":"Service not found"}}}},"/api/reviews":{"get":{"summary":"List reviews (optionally filtered by service or pro)","tags":["Reviews"],"parameters":[{"in":"query","name":"serviceId","schema":{"type":"string","format":"uuid"}},{"in":"query","name":"proId","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"List of reviews","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Review"}}}}}}}},"/api/bookings/{id}/review":{"post":{"summary":"Leave a review for a completed booking (customer)","tags":["Reviews"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["rating"],"properties":{"rating":{"type":"integer","minimum":1,"maximum":5},"text":{"type":"string"}}}}}},"responses":{"201":{"description":"Review created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Review"}}}}}}},"/api/professionals":{"get":{"summary":"List all professionals","tags":["Professionals"],"responses":{"200":{"description":"List of professionals","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Professional"}}}}}}},"post":{"summary":"Create a professional (admin)","tags":["Professionals"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","title"]}}}},"responses":{"201":{"description":"Professional created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Professional"}}}}}}},"/api/professionals/{id}":{"get":{"summary":"Get a single professional by id","tags":["Professionals"],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Professional found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Professional"}}}},"404":{"description":"Professional not found"}}},"put":{"summary":"Update a professional (admin)","tags":["Professionals"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Professional updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Professional"}}}}}},"delete":{"summary":"Delete a professional (admin)","tags":["Professionals"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Professional deleted"}}}},"/api/professionals/{id}/reviews":{"get":{"summary":"List reviews for a professional","tags":["Professionals"],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"List of reviews","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Review"}}}}}}}},"/api/invoices":{"get":{"summary":"List invoices (customers see own, staff see all)","tags":["Invoices"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"query","name":"status","schema":{"$ref":"#/components/schemas/InvoiceStatus"}}],"responses":{"200":{"description":"List of invoices","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Invoice"}}}}}}}},"/api/invoices/{id}":{"get":{"summary":"Get a single invoice","tags":["Invoices"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Invoice found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Invoice"}}}},"404":{"description":"Invoice not found"}}}},"/api/invoices/{id}/pay":{"post":{"summary":"Mark an invoice as paid (customer)","tags":["Invoices"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["method"],"properties":{"method":{"type":"string"}}}}}},"responses":{"200":{"description":"Invoice marked paid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Invoice"}}}}}}},"/api/bookings":{"get":{"summary":"List bookings (customers see own, staff see all)","tags":["Bookings"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"query","name":"status","schema":{"$ref":"#/components/schemas/BookingStatus"}}],"responses":{"200":{"description":"List of bookings","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Booking"}}}}}}},"post":{"summary":"Create a booking (customer)","tags":["Bookings"],"security":[{"bearerAuth":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["serviceId","date","time","address","frequency","hours","price"],"properties":{"serviceId":{"type":"string","format":"uuid"},"date":{"type":"string","format":"date"},"time":{"type":"string"},"address":{"type":"string"},"frequency":{"$ref":"#/components/schemas/Frequency"},"hours":{"type":"integer"},"notes":{"type":"string"},"price":{"type":"integer"}}}}}},"responses":{"201":{"description":"Booking created with status 'confirmed'","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}}}}},"/api/bookings/{id}":{"get":{"summary":"Get a single booking","tags":["Bookings"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Booking found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"404":{"description":"Booking not found"}}}},"/api/bookings/{id}/assign":{"post":{"summary":"Assign a professional to a booking (staff)","tags":["Bookings"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["proId"],"properties":{"proId":{"type":"string","format":"uuid"}}}}}},"responses":{"200":{"description":"Booking assigned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}}}}},"/api/bookings/{id}/advance":{"post":{"summary":"Advance booking status one step forward (staff)","tags":["Bookings"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Booking advanced","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}}}}},"/api/bookings/{id}/messages":{"get":{"summary":"List messages for a booking","tags":["Bookings"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"List of messages","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Message"}}}}}}},"post":{"summary":"Send a message on a booking","tags":["Bookings"],"security":[{"bearerAuth":[]}],"parameters":[{"in":"path","name":"id","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["sender","text"],"properties":{"sender":{"$ref":"#/components/schemas/MessageFrom"},"text":{"type":"string"}}}}}},"responses":{"201":{"description":"Message created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Message"}}}}}}},"/api/auth/register":{"post":{"summary":"Register a new customer account","tags":["Auth"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name","email","password"],"properties":{"name":{"type":"string"},"email":{"type":"string","format":"email"},"password":{"type":"string","minLength":6}}}}}},"responses":{"201":{"description":"Account created","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"user":{"$ref":"#/components/schemas/User"}}}}}},"409":{"description":"Email already registered","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/auth/login":{"post":{"summary":"Log in and receive a JWT","tags":["Auth"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email"},"password":{"type":"string"}}}}}},"responses":{"200":{"description":"Successful login","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"user":{"$ref":"#/components/schemas/User"}}}}}},"401":{"description":"Invalid credentials"}}}},"/api/auth/me":{"get":{"summary":"Get the currently authenticated user","tags":["Auth"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Current user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}},"401":{"description":"Unauthenticated"}}}},"/api/admin/stats":{"get":{"summary":"Dashboard statistics (admin/employee)","tags":["Admin"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggregated statistics","content":{"application/json":{"schema":{"type":"object","properties":{"newBookings":{"type":"integer"},"active":{"type":"integer"},"completedToday":{"type":"integer"},"revenue":{"type":"integer"},"customerCount":{"type":"integer"},"openInvoicesTotal":{"type":"integer"}}}}}}}}},"/api/admin/customers":{"get":{"summary":"List all customers (admin/employee)","tags":["Admin"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"List of customers","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/User"}}}}}}}},"/api/admin/pros":{"get":{"summary":"List all professionals (admin/employee)","tags":["Admin"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"List of professionals","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Professional"}}}}}}}},"/health":{"get":{"summary":"Health check","tags":["Health"],"responses":{"200":{"description":"Service is healthy","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"uptime":{"type":"number"}}}}}}}}}},"tags":[]}