{"openapi":"3.1.0","info":{"title":"Studio Platform API","version":"0.1.0","description":"Booking + storefront API for rehearsal studios. Storefront routes are org-scoped by slug; admin routes derive the org from the staff session."},"servers":[{"url":"http://api:3001"}],"components":{"schemas":{"StorefrontInfo":{"type":"object","properties":{"org":{"type":"object","properties":{"name":{"type":"string"},"slug":{"type":"string"},"cancellationWindowHours":{"type":"integer"},"refundMode":{"type":"string","enum":["card","credit"]},"theme":{"type":"object","additionalProperties":{}}},"required":["name","slug","cancellationWindowHours","refundMode","theme"]},"location":{"type":"object","properties":{"name":{"type":"string"},"addressLine1":{"type":"string"},"addressLine2":{"type":["string","null"]},"city":{"type":"string"},"region":{"type":"string"},"postalCode":{"type":"string"},"country":{"type":"string"},"phone":{"type":["string","null"]},"email":{"type":["string","null"]},"timezone":{"type":"string"},"hours":{"type":"array","items":{"type":"object","properties":{"dayOfWeek":{"type":"integer","minimum":0,"maximum":6},"opensAt":{"type":"string"},"closesAt":{"type":"string"}},"required":["dayOfWeek","opensAt","closesAt"]}}},"required":["name","addressLine1","addressLine2","city","region","postalCode","country","phone","email","timezone","hours"]}},"required":["org","location"]},"ApiError":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]},"StorefrontRoomsResponse":{"type":"object","properties":{"rooms":{"type":"array","items":{"$ref":"#/components/schemas/StorefrontRoom"}}},"required":["rooms"]},"StorefrontRoom":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"locationId":{"type":"string","format":"uuid"},"name":{"type":"string"},"code":{"type":["string","null"]},"mode":{"type":"string","enum":["hourly","monthly","both"]},"approvalPolicy":{"type":"string","enum":["instant","approval_required"]},"dimensions":{"type":["string","null"]},"sqft":{"type":["integer","null"]},"photos":{"type":"array","items":{"type":"string"}},"tier":{"$ref":"#/components/schemas/RoomTier"},"ratePlans":{"type":"array","items":{"$ref":"#/components/schemas/RatePlan"}}},"required":["id","locationId","name","code","mode","approvalPolicy","dimensions","sqft","photos","tier","ratePlans"]},"RoomTier":{"type":["object","null"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"pitch":{"type":["string","null"]},"description":{"type":["string","null"]},"capacityMin":{"type":["integer","null"]},"capacityMax":{"type":["integer","null"]},"gear":{"type":"array","items":{"type":"string"}},"amenities":{"type":"array","items":{"type":"string"}}},"required":["id","name","pitch","description","capacityMin","capacityMax","gear","amenities"]},"RatePlan":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"kind":{"type":"string","enum":["hourly","monthly","both"]},"activityId":{"type":["string","null"],"format":"uuid"},"priceCents":{"type":"integer"},"peakPriceCents":{"type":["integer","null"]},"peakStartHour":{"type":["integer","null"]}},"required":["id","kind","activityId","priceCents","peakPriceCents","peakStartHour"]},"DayAvailability":{"type":"object","properties":{"roomId":{"type":"string","format":"uuid"},"date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-08-12"},"timezone":{"type":"string"},"window":{"type":["object","null"],"properties":{"start":{"type":"string"},"end":{"type":"string"}},"required":["start","end"]},"starts":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string"},"ends":{"type":"array","items":{"type":"string"}}},"required":["start","ends"]}}},"required":["roomId","date","timezone","window","starts"]},"StorefrontBooking":{"allOf":[{"$ref":"#/components/schemas/Booking"},{"type":"object","properties":{"timezone":{"type":"string"}},"required":["timezone"]}]},"Booking":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"roomId":{"type":"string","format":"uuid"},"locationId":{"type":"string","format":"uuid"},"customerId":{"type":"string","format":"uuid"},"activityId":{"type":["string","null"],"format":"uuid"},"seriesId":{"type":["string","null"],"format":"uuid"},"startsAt":{"type":"string"},"endsAt":{"type":"string"},"status":{"type":"string","enum":["pending_approval","confirmed","declined","cancelled","completed","no_show"]},"subtotalCents":{"type":"integer"},"taxCents":{"type":"integer"},"totalCents":{"type":"integer"},"creditAppliedCents":{"type":"integer"},"paymentStatus":{"type":"string","enum":["unpaid","authorized","paid","refunded","credit_refunded","failed"]},"gearRequest":{"type":["string","null"]},"notes":{"type":["string","null"]},"source":{"type":"string","enum":["storefront","admin","api","series"]},"cancelledAt":{"type":["string","null"]},"cancelReason":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","roomId","locationId","customerId","activityId","seriesId","startsAt","endsAt","status","subtotalCents","taxCents","totalCents","creditAppliedCents","paymentStatus","gearRequest","notes","source","cancelledAt","cancelReason","createdAt"]},"CustomerBookingsResponse":{"type":"object","properties":{"bookings":{"type":"array","items":{"$ref":"#/components/schemas/CustomerBooking"}},"creditCents":{"type":"integer"}},"required":["bookings","creditCents"]},"CustomerBooking":{"allOf":[{"$ref":"#/components/schemas/StorefrontBooking"},{"type":"object","properties":{"roomName":{"type":"string"},"roomCode":{"type":["string","null"]}},"required":["roomName","roomCode"]}]},"CancelBookingResponse":{"type":"object","properties":{"booking":{"$ref":"#/components/schemas/StorefrontBooking"},"refundable":{"type":"boolean"},"creditIssuedCents":{"type":"integer"}},"required":["booking","refundable","creditIssuedCents"]},"RecurringSeries":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"roomId":{"type":"string","format":"uuid"},"customerId":{"type":"string","format":"uuid"},"activityId":{"type":["string","null"],"format":"uuid"},"rrule":{"type":"string"},"dtstart":{"type":"string"},"durationMinutes":{"type":"integer"},"priceCentsPerOccurrence":{"type":"integer"},"status":{"type":"string","enum":["active","paused","cancelled"]},"generatedThrough":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","roomId","customerId","activityId","rrule","dtstart","durationMinutes","priceCentsPerOccurrence","status","generatedThrough","createdAt"]},"PayBookingResponse":{"type":"object","properties":{"clientSecret":{"type":["string","null"]},"paymentIntentId":{"type":["string","null"]},"creditAppliedCents":{"type":"integer"},"cardChargeCents":{"type":"integer"},"paymentStatus":{"type":"string","enum":["unpaid","authorized","paid","refunded","credit_refunded","failed"]}},"required":["clientSecret","paymentIntentId","creditAppliedCents","cardChargeCents","paymentStatus"]},"PayBookingRequest":{"type":"object","properties":{"saveCard":{"type":"boolean"}}},"SetupIntentResponse":{"type":"object","properties":{"clientSecret":{"type":["string","null"]},"setupIntentId":{"type":"string"},"stripeCustomerId":{"type":"string"}},"required":["clientSecret","setupIntentId","stripeCustomerId"]},"Organization":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":"string"},"cancellationWindowHours":{"type":"integer"},"refundMode":{"type":"string","enum":["card","credit"]},"createdAt":{"type":"string"}},"required":["id","name","slug","cancellationWindowHours","refundMode","createdAt"]},"CreateOrgRequest":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"slug":{"type":"string","minLength":2,"maxLength":60,"pattern":"^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$"},"timezone":{"type":"string"}},"required":["name","slug","timezone"]},"CalendarDay":{"type":"object","properties":{"date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-08-12"},"rooms":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"locationId":{"type":"string","format":"uuid"},"timezone":{"type":"string"},"name":{"type":"string"},"code":{"type":["string","null"]},"bookings":{"type":"array","items":{"$ref":"#/components/schemas/CalendarBooking"}}},"required":["id","locationId","timezone","name","code","bookings"]}}},"required":["date","rooms"]},"CalendarBooking":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"startsAt":{"type":"string"},"endsAt":{"type":"string"},"status":{"type":"string","enum":["pending_approval","confirmed","declined","cancelled","completed","no_show"]},"customerName":{"type":"string"},"phone":{"type":["string","null"]},"totalCents":{"type":"integer"},"paymentStatus":{"type":"string","enum":["unpaid","authorized","paid","refunded","credit_refunded","failed"]},"notes":{"type":["string","null"]},"gearRequest":{"type":["string","null"]}},"required":["id","startsAt","endsAt","status","customerName","phone","totalCents","paymentStatus","notes","gearRequest"]},"AdminBookingDetail":{"allOf":[{"$ref":"#/components/schemas/Booking"},{"type":"object","properties":{"customer":{"$ref":"#/components/schemas/Customer"},"series":{"allOf":[{"$ref":"#/components/schemas/RecurringSeries"},{"type":["object","null"]}]}},"required":["customer","series"]}]},"Customer":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"email":{"type":"string"},"phone":{"type":["string","null"]},"bandName":{"type":["string","null"]},"creditCents":{"type":"integer"},"notes":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","name","email","phone","bandName","creditCents","notes","createdAt"]},"AdminCreateBookingRequest":{"type":"object","properties":{"roomId":{"type":"string","format":"uuid"},"activityId":{"type":"string","format":"uuid"},"customerId":{"type":"string","format":"uuid"},"customer":{"type":"object","properties":{"name":{"type":"string","minLength":1},"email":{"type":"string","format":"email"}},"required":["name","email"]},"startsAt":{"type":"string","format":"date-time","example":"2026-08-13T02:00:00.000Z"},"endsAt":{"type":"string","format":"date-time","example":"2026-08-13T02:00:00.000Z"},"gearRequest":{"type":"string","maxLength":2000},"notes":{"type":"string","maxLength":4000}},"required":["roomId","startsAt","endsAt"]},"AdminUpdateBookingRequest":{"type":"object","properties":{"startsAt":{"type":"string","format":"date-time","example":"2026-08-13T02:00:00.000Z"},"endsAt":{"type":"string","format":"date-time","example":"2026-08-13T02:00:00.000Z"},"roomId":{"type":"string","format":"uuid"}}},"AdminCancelBookingRequest":{"type":"object","properties":{"reason":{"type":"string","maxLength":1000}}},"ApprovalBooking":{"allOf":[{"$ref":"#/components/schemas/Booking"},{"type":"object","properties":{"customerName":{"type":"string"},"roomName":{"type":"string"}},"required":["customerName","roomName"]}]},"AdminDeclineBookingRequest":{"type":"object","properties":{"reason":{"type":"string","maxLength":1000}}},"AdminRoom":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"locationId":{"type":"string","format":"uuid"},"name":{"type":"string"},"code":{"type":["string","null"]},"mode":{"type":"string","enum":["hourly","monthly","both"]},"approvalPolicy":{"type":"string","enum":["instant","approval_required"]},"bufferMinutes":{"type":"integer"},"sortOrder":{"type":"integer"},"active":{"type":"boolean"}},"required":["id","locationId","name","code","mode","approvalPolicy","bufferMinutes","sortOrder","active"]},"AdminUpdateRoomRequest":{"type":"object","properties":{"approvalPolicy":{"type":"string","enum":["instant","approval_required"]},"bufferMinutes":{"type":"integer","minimum":0,"maximum":240},"active":{"type":"boolean"}}},"RoomPhotoUploadResponse":{"allOf":[{"$ref":"#/components/schemas/RoomPhotos"},{"type":"object","properties":{"urls":{"$ref":"#/components/schemas/RoomPhotoUrls"}},"required":["urls"]}]},"RoomPhotoUrls":{"type":"object","properties":{"thumb":{"type":"string"},"card":{"type":"string"},"full":{"type":"string"}},"required":["thumb","card","full"]},"RoomPhotos":{"type":"object","properties":{"roomId":{"type":"string","format":"uuid"},"photos":{"type":"array","items":{"type":"string"}}},"required":["roomId","photos"]},"AdminDeleteRoomPhotoRequest":{"type":"object","properties":{"url":{"type":"string","minLength":1}},"required":["url"]},"AdminOrg":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":"string"},"theme":{"type":"object","additionalProperties":{}},"cancellationWindowHours":{"type":"integer"},"refundMode":{"type":"string","enum":["card","credit"]},"requireStaffTwoFactor":{"type":"boolean"},"chargesEnabled":{"type":"boolean"},"subscriptionStatus":{"type":["string","null"]},"createdAt":{"type":"string"}},"required":["id","name","slug","theme","cancellationWindowHours","refundMode","requireStaffTwoFactor","chargesEnabled","subscriptionStatus","createdAt"]},"AdminUpdateOrgRequest":{"type":"object","properties":{"theme":{"$ref":"#/components/schemas/Theme"},"cancellationWindowHours":{"type":"integer","minimum":0,"maximum":8760},"refundMode":{"type":"string","enum":["card","credit"]},"requireStaffTwoFactor":{"type":"boolean"}}},"Theme":{"type":"object","properties":{"preset":{"type":"string","enum":["daylight","stage","tape"]},"accent":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"display":{"type":"string","minLength":1,"maxLength":120}},"required":["preset"],"additionalProperties":false},"StripeConnectOnboarding":{"type":"object","properties":{"accountId":{"type":"string"},"url":{"type":"string"}},"required":["accountId","url"]},"StripeConnectStatus":{"type":"object","properties":{"connected":{"type":"boolean"},"accountId":{"type":["string","null"]},"chargesEnabled":{"type":"boolean"},"detailsSubmitted":{"type":"boolean"}},"required":["connected","accountId","chargesEnabled","detailsSubmitted"]},"PlatformBillingSubscription":{"type":"object","properties":{"subscriptionId":{"type":"string"},"status":{"type":"string"},"quantity":{"type":"integer"}},"required":["subscriptionId","status","quantity"]},"AdminRefundResponse":{"type":"object","properties":{"booking":{"$ref":"#/components/schemas/Booking"},"mode":{"type":"string","enum":["card","credit"]},"refundedCents":{"type":"integer"}},"required":["booking","mode","refundedCents"]},"AdminRefundRequest":{"type":"object","properties":{"mode":{"type":"string","enum":["card","credit"]}}}},"parameters":{}},"paths":{"/api/storefront/{slug}/info":{"get":{"operationId":"storefrontGetInfo","summary":"Org storefront profile: policies + theme + primary location (address, contact, hours)","tags":["Storefront"],"parameters":[{"schema":{"type":"string","example":"music-loft"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"Storefront info","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StorefrontInfo"}}}},"404":{"description":"Org/location not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/storefront/{slug}/rooms":{"get":{"operationId":"storefrontListRooms","summary":"List rooms with tier info and rate plans","tags":["Storefront"],"parameters":[{"schema":{"type":"string","example":"music-loft"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"Rooms","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StorefrontRoomsResponse"}}}},"404":{"description":"Org not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/storefront/{slug}/availability":{"get":{"operationId":"storefrontGetAvailability","summary":"Bookable start instants + valid ends for a room on a local date","tags":["Storefront"],"parameters":[{"schema":{"type":"string","example":"music-loft"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"roomId","in":"query"},{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-08-12"},"required":true,"name":"date","in":"query"}],"responses":{"200":{"description":"Availability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DayAvailability"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/storefront/{slug}/bookings":{"post":{"operationId":"storefrontCreateBooking","summary":"Book a room (customer)","tags":["Storefront"],"parameters":[{"schema":{"type":"string","example":"music-loft"},"required":true,"name":"slug","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"roomId":{"type":"string","format":"uuid"},"activityId":{"type":"string","format":"uuid"},"startsAt":{"type":"string","format":"date-time","example":"2026-08-13T02:00:00.000Z"},"endsAt":{"type":"string","format":"date-time","example":"2026-08-13T02:00:00.000Z"},"gearRequest":{"type":"string","maxLength":2000}},"required":["roomId","startsAt","endsAt"]}}}},"responses":{"201":{"description":"Booking created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StorefrontBooking"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Slot unavailable/taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"get":{"operationId":"storefrontListMyBookings","summary":"The signed-in customer's bookings (newest first, capped at 100) + their account credit balance (creditCents)","tags":["Storefront"],"parameters":[{"schema":{"type":"string","example":"music-loft"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"Bookings + credit balance","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerBookingsResponse"}}}}}}},"/api/storefront/{slug}/bookings/{id}/cancel":{"post":{"operationId":"storefrontCancelBooking","summary":"Cancel one of the customer's own future bookings","tags":["Storefront"],"parameters":[{"schema":{"type":"string","example":"music-loft"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelBookingResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Not cancellable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"Stripe refund failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/storefront/{slug}/series":{"post":{"operationId":"storefrontCreateSeries","summary":"Create a recurring weekly slot (occurrences are generated by the jobs worker)","tags":["Storefront"],"parameters":[{"schema":{"type":"string","example":"music-loft"},"required":true,"name":"slug","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"roomId":{"type":"string","format":"uuid"},"activityId":{"type":"string","format":"uuid"},"rrule":{"type":"string","example":"FREQ=WEEKLY;BYDAY=TU"},"dtstart":{"type":"string","format":"date-time","example":"2026-08-13T02:00:00.000Z"},"durationMinutes":{"type":"integer","minimum":15,"maximum":1440}},"required":["roomId","rrule","dtstart","durationMinutes"]}}}},"responses":{"201":{"description":"Series created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecurringSeries"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/storefront/{slug}/bookings/{id}/pay":{"post":{"operationId":"storefrontPayBooking","summary":"Create the PaymentIntent for a booking's remaining card charge (credit applied first)","tags":["Storefront","Payments"],"parameters":[{"schema":{"type":"string","example":"riff-city"},"required":true,"name":"slug","in":"path"},{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayBookingRequest"}}}},"responses":{"200":{"description":"Payment intent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayBookingResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Not payable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"Stripe error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/storefront/{slug}/setup-intent":{"post":{"operationId":"storefrontCreateSetupIntent","summary":"SetupIntent for saving a card off-session (recurring series / monthly lockout)","tags":["Storefront","Payments"],"parameters":[{"schema":{"type":"string","example":"riff-city"},"required":true,"name":"slug","in":"path"}],"responses":{"200":{"description":"Setup intent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetupIntentResponse"}}}},"409":{"description":"Payments not configured","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"Stripe error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/admin/orgs":{"post":{"operationId":"adminCreateOrg","summary":"Bootstrap an organization (staff session, no existing org): app org + default location + auth-plugin org/member","tags":["Admin"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrgRequest"}}}},"responses":{"201":{"description":"Organization created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Organization"}}}},"401":{"description":"No staff session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Slug taken / user already has an org","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/admin/calendar":{"get":{"operationId":"adminGetCalendar","summary":"All rooms with a local day's bookings — the dashboard calendar feed","tags":["Admin"],"parameters":[{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-08-12"},"required":true,"name":"date","in":"query"},{"schema":{"type":"string","format":"uuid"},"required":false,"name":"locationId","in":"query"}],"responses":{"200":{"description":"Calendar","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarDay"}}}},"404":{"description":"Location not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/admin/bookings/{id}":{"get":{"operationId":"adminGetBooking","summary":"Full booking detail: money fields, customer (incl. phone/email), series link","tags":["Admin"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Booking","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminBookingDetail"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"patch":{"operationId":"adminUpdateBooking","summary":"Move/resize a booking (re-gated against availability, excluding itself)","tags":["Admin"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminUpdateBookingRequest"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Slot unavailable/taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/admin/bookings":{"post":{"operationId":"adminCreateBooking","summary":"Staff create a booking (existing customerId or inline {name,email})","tags":["Admin"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminCreateBookingRequest"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Slot unavailable/taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/admin/bookings/{id}/cancel":{"post":{"operationId":"adminCancelBooking","summary":"Staff cancel a booking","tags":["Admin"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminCancelBookingRequest"}}}},"responses":{"200":{"description":"Cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Not cancellable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/admin/approvals":{"get":{"operationId":"adminListApprovals","summary":"Bookings awaiting approval","tags":["Admin"],"responses":{"200":{"description":"Pending approvals","content":{"application/json":{"schema":{"type":"object","properties":{"bookings":{"type":"array","items":{"$ref":"#/components/schemas/ApprovalBooking"}}},"required":["bookings"]}}}}}}},"/api/admin/approvals/{id}/approve":{"post":{"operationId":"adminApproveBooking","summary":"Approve a pending booking","tags":["Admin"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Approved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"404":{"description":"Not found / not pending","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Card capture failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/admin/approvals/{id}/decline":{"post":{"operationId":"adminDeclineBooking","summary":"Decline a pending booking","tags":["Admin"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminDeclineBookingRequest"}}}},"responses":{"200":{"description":"Declined","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Booking"}}}},"404":{"description":"Not found / not pending","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/admin/customers":{"get":{"operationId":"adminSearchCustomers","summary":"Search customers by name/email (org-scoped, limit 20)","tags":["Admin"],"parameters":[{"schema":{"type":"string"},"required":false,"name":"query","in":"query"}],"responses":{"200":{"description":"Customers","content":{"application/json":{"schema":{"type":"object","properties":{"customers":{"type":"array","items":{"$ref":"#/components/schemas/Customer"}}},"required":["customers"]}}}}}}},"/api/admin/rooms":{"get":{"operationId":"adminListRooms","summary":"All rooms for the org (including inactive)","tags":["Admin"],"responses":{"200":{"description":"Rooms","content":{"application/json":{"schema":{"type":"object","properties":{"rooms":{"type":"array","items":{"$ref":"#/components/schemas/AdminRoom"}}},"required":["rooms"]}}}}}}},"/api/admin/rooms/{id}":{"patch":{"operationId":"adminUpdateRoom","summary":"Update room booking policy settings","tags":["Admin"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminUpdateRoomRequest"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminRoom"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/admin/rooms/{id}/photos":{"post":{"operationId":"adminUploadRoomPhoto","summary":"Upload a room photo (multipart field `file`, or raw image bytes). Re-encoded to webp at 400/1024/1920; the card (1024) url is appended to room.photos — derive variants by swapping the -1024 suffix.","tags":["Admin"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"201":{"description":"Photo stored","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomPhotoUploadResponse"}}}},"400":{"description":"Missing/invalid/oversized file","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"Room not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Photo limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}},"delete":{"operationId":"adminDeleteRoomPhoto","summary":"Remove a photo url from the room and delete the stored variants (best-effort)","tags":["Admin"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminDeleteRoomPhotoRequest"}}}},"responses":{"200":{"description":"Removed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoomPhotos"}}}},"404":{"description":"Room/photo not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/admin/org":{"get":{"operationId":"adminGetOrg","summary":"Current org settings (theme, policies, billing status)","tags":["Admin"],"responses":{"200":{"description":"Org","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminOrg"}}}}}},"patch":{"operationId":"adminUpdateOrg","summary":"Update org settings. theme is strict ({preset, accent?, display?}) and MERGES over the existing theme jsonb — unmanaged keys survive.","tags":["Admin"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminUpdateOrgRequest"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminOrg"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/admin/series":{"get":{"operationId":"adminListSeries","summary":"All recurring series for the org","tags":["Admin"],"responses":{"200":{"description":"Series","content":{"application/json":{"schema":{"type":"object","properties":{"series":{"type":"array","items":{"$ref":"#/components/schemas/RecurringSeries"}}},"required":["series"]}}}}}}},"/api/admin/series/{id}/pause":{"post":{"operationId":"adminPauseSeries","summary":"Pause an active series","tags":["Admin"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Paused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecurringSeries"}}}},"404":{"description":"Not found / not active","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/admin/series/{id}/resume":{"post":{"operationId":"adminResumeSeries","summary":"Resume a paused series","tags":["Admin"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Resumed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecurringSeries"}}}},"404":{"description":"Not found / not paused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/admin/stripe/connect":{"post":{"operationId":"adminStripeConnect","summary":"Create the org's Stripe Express account (if missing) and return an onboarding link","tags":["Admin","Payments"],"responses":{"200":{"description":"Onboarding link","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StripeConnectOnboarding"}}}},"502":{"description":"Stripe error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/admin/stripe/status":{"get":{"operationId":"adminStripeStatus","summary":"Live Connect account status (charges_enabled / details_submitted)","tags":["Admin","Payments"],"responses":{"200":{"description":"Status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StripeConnectStatus"}}}},"502":{"description":"Stripe error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/admin/billing/subscribe":{"post":{"operationId":"adminBillingSubscribe","summary":"Create/update the platform's $20/room/mo subscription (quantity = active rooms)","tags":["Admin","Payments"],"responses":{"200":{"description":"Subscription","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlatformBillingSubscription"}}}},"502":{"description":"Stripe error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/api/admin/bookings/{id}/refund":{"post":{"operationId":"adminRefundBooking","summary":"Manually refund a paid booking (overrides the cancellation window)","tags":["Admin","Payments"],"parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminRefundRequest"}}}},"responses":{"200":{"description":"Refunded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminRefundResponse"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"Not refundable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"502":{"description":"Stripe error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}}},"webhooks":{}}