{"openapi":"3.0.0","paths":{"/api/v1/auth/firebase":{"post":{"operationId":"AuthController_firebase","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FirebaseExchangeDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResponseDto"}}}},"401":{"description":"AUTH_INVALID_TOKEN","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"summary":"Exchange a Firebase phone-auth ID token for Chale tokens","tags":["auth"]}},"/api/v1/auth/refresh":{"post":{"operationId":"AuthController_refresh","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResponseDto"}}}},"401":{"description":"AUTH_INVALID_TOKEN","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"summary":"Rotate the refresh token; reuse revokes the family","tags":["auth"]}},"/api/v1/auth/logout":{"post":{"operationId":"AuthController_logout","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshDto"}}}},"responses":{"204":{"description":""}},"summary":"Revoke the refresh-token family","tags":["auth"]}},"/api/v1/.well-known/jwks.json":{"get":{"operationId":"JwksController_jwks","parameters":[],"responses":{"200":{"description":""}},"summary":"Public keys for verifying Chale access tokens (RS256)","tags":["auth"]}},"/health":{"get":{"operationId":"HealthController_health","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponseDto"}}}}},"summary":"Liveness: the process is up","tags":["health"]}},"/ready":{"get":{"operationId":"HealthController_ready","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReadyResponseDto"}}}},"503":{"description":"DEPENDENCY_UNAVAILABLE","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"summary":"Readiness: database and Redis respond","tags":["health"]}},"/api/v1/businesses":{"post":{"operationId":"BusinessController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBusinessDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessResponseDto"}}}},"403":{"description":"FORBIDDEN (API keys cannot create businesses)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"VALIDATION_FAILED · UNKNOWN_VERTICAL","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Create a business; the caller becomes its OWNER","tags":["businesses"]}},"/api/v1/businesses/me":{"get":{"operationId":"BusinessController_mine","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessListResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Businesses the caller is a member of","tags":["businesses"]}},"/api/v1/businesses/{businessId}":{"get":{"operationId":"BusinessController_get","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessResponseDto"}}}},"404":{"description":"NOT_FOUND (also for another tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"One business (members, its API key, operators)","tags":["businesses"]},"patch":{"operationId":"BusinessController_update","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateBusinessDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessResponseDto"}}}},"403":{"description":"ROLE_REQUIRED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"NOT_FOUND (also for another tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"VALIDATION_FAILED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Update profile and policies (OWNER, or a key with business:write)","tags":["businesses"]}},"/api/v1/businesses/{businessId}/location":{"put":{"operationId":"BusinessController_setLocation","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetLocationDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessResponseDto"}}}},"403":{"description":"ROLE_REQUIRED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"NOT_FOUND (also for another tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"VALIDATION_FAILED (coordinates, area)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Set the pin, address and area (OWNER); area derived from the nearest centroid ≤ 8 km","tags":["businesses"]}},"/api/v1/businesses/{businessId}/assistant-status":{"put":{"operationId":"BusinessController_setAssistantStatus","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistantStatusDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"The assistant's key reports whether it answers here and from which number (Epic 11, F77)","tags":["businesses"]}},"/api/v1/businesses/{businessId}/listing":{"patch":{"operationId":"BusinessController_updateListing","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateListingDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessResponseDto"}}}},"422":{"description":"POLICY_VIOLATION when PUBLIC is not possible yet (details list what is missing)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Listing controls: public/hidden, description, photos (OWNER, TICKET-109)","tags":["businesses"]}},"/api/v1/businesses/{businessId}/members":{"get":{"operationId":"MembersController_list","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberListResponseDto"}}}},"404":{"description":"NOT_FOUND (also for another tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Roster of the business (OWNER and STAFF)","tags":["members"]},"post":{"operationId":"MembersController_invite","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteMemberDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberResponseDto"}}}},"403":{"description":"ROLE_REQUIRED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"ALREADY_EXISTS","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Invite a phone number as OWNER or STAFF (PENDING until first sign-in)","tags":["members"]}},"/api/v1/businesses/{businessId}/members/{memberId}":{"delete":{"operationId":"MembersController_remove","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"memberId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Removed"},"403":{"description":"ROLE_REQUIRED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"LAST_OWNER","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Remove a member (never the last ACTIVE OWNER)","tags":["members"]}},"/api/v1/businesses/{businessId}/hours":{"get":{"operationId":"ScheduleController_hours","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoursListResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Weekly opening hours (7 rows, or empty before the first PUT)","tags":["businesses"]},"put":{"operationId":"ScheduleController_putHours","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutHoursDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoursListResponseDto"}}}},"403":{"description":"ROLE_REQUIRED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"VALIDATION_FAILED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Replace the weekly hours (exactly 7 entries; open < close when enabled)","tags":["businesses"]}},"/api/v1/businesses/{businessId}/closures":{"get":{"operationId":"ScheduleController_closures","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClosureListResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Closed date ranges, earliest first","tags":["businesses"]},"post":{"operationId":"ScheduleController_createClosure","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateClosureDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClosureResponseDto"}}}},"403":{"description":"ROLE_REQUIRED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"VALIDATION_FAILED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Add a closure; overlapping or touching closures are merged into one","tags":["businesses"]}},"/api/v1/businesses/{businessId}/closures/{closureId}":{"delete":{"operationId":"ScheduleController_deleteClosure","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"closureId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Removed"},"404":{"description":"NOT_FOUND (also for another tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Remove a closure","tags":["businesses"]}},"/api/v1/vertical-profiles":{"get":{"operationId":"VerticalProfilesController_list","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerticalProfileListResponseDto"}}}}},"summary":"Enabled vertical profiles","tags":["reference"]}},"/api/v1/areas":{"get":{"operationId":"GeoController_list","parameters":[{"name":"city","required":false,"in":"query","description":"Case-insensitive city filter","schema":{"example":"accra","type":"string"}},{"name":"q","required":false,"in":"query","description":"Case-insensitive name prefix","schema":{"example":"east","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AreaListResponseDto"}}}}},"summary":"Curated areas (Accra, Tema, Kasoa), filter by city and name prefix","tags":["reference"]}},"/api/v1/businesses/{businessId}/delivery-zones":{"get":{"operationId":"DeliveryZonesController_list","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryZoneListResponseDto"}}}},"404":{"description":"NOT_FOUND (also for another tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Delivery zones (area + fee) of the business","tags":["businesses"]},"put":{"operationId":"DeliveryZonesController_replace","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutDeliveryZonesDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryZoneListResponseDto"}}}},"403":{"description":"ROLE_REQUIRED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"VALIDATION_FAILED (duplicate or unknown area, fee)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Replace the delivery zones (bulk upsert, idempotent; unlisted zones are removed)","tags":["businesses"]}},"/api/v1/businesses/{businessId}/api-keys":{"post":{"operationId":"ApiKeysController_create","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateApiKeyDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyResponseDto"}}}},"403":{"description":"ROLE_REQUIRED · FORBIDDEN","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Issue a scoped API key; the plaintext is returned once","tags":["api-keys"]},"get":{"operationId":"ApiKeysController_list","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyListResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"List keys (no secrets)","tags":["api-keys"]}},"/api/v1/businesses/{businessId}/api-keys/{keyId}":{"delete":{"operationId":"ApiKeysController_revoke","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"keyId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Revoked"},"404":{"description":"NOT_FOUND (also for another tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Revoke a key (idempotent)","tags":["api-keys"]}},"/api/v1/businesses/{businessId}/api-usage":{"get":{"operationId":"ApiUsageController_usageFor","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"from","required":false,"in":"query","schema":{"example":"2026-09-01","type":"string"}},{"name":"to","required":false,"in":"query","schema":{"example":"2026-09-30","type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Requests per day per key (default the last 30 days; range ≤ 92 days)","tags":["api-keys"]}},"/api/v1/admin/businesses":{"get":{"operationId":"AdminController_listBusinesses","parameters":[{"name":"q","required":false,"in":"query","description":"Case-insensitive match on name or slug","schema":{"example":"bounce","type":"string"}},{"name":"status","required":false,"in":"query","schema":{"type":"string","enum":["DRAFT","PENDING_VERIFICATION","ACTIVE","SUSPENDED"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminBusinessListResponseDto"}}}},"403":{"description":"ROLE_REQUIRED · FORBIDDEN (API keys)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"All businesses (operators), cursor-paginated","tags":["admin"]}},"/api/v1/admin/businesses/{businessId}":{"get":{"operationId":"AdminController_getBusiness","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BusinessResponseDto"}}}},"404":{"description":"NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"One business, any tenant (operators)","tags":["admin"]}},"/api/v1/admin/businesses/{businessId}/overview":{"get":{"operationId":"AdminActionsController_overview","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Business + members + keys (no secrets) + catalogue/booking counts (operators)","tags":["admin"]}},"/api/v1/admin/businesses/{businessId}/status":{"post":{"operationId":"AdminActionsController_setStatus","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminStatusDto"}}}},"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Verify (ACTIVE), send back (PENDING_VERIFICATION) or suspend a business — audited","tags":["admin"]}},"/api/v1/admin/businesses/{businessId}/listing":{"post":{"operationId":"AdminActionsController_setListing","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminListingDto"}}}},"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Force the listing flag (publish, or take down with a reason that locks the owner switch) — audited","tags":["admin"]}},"/api/v1/admin/businesses/{businessId}/api-keys":{"get":{"operationId":"AdminActionsController_listKeys","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"A business's API keys — prefix, scopes, last use; never a secret (operators)","tags":["admin"]}},"/api/v1/admin/usage":{"get":{"operationId":"AdminActionsController_usage","parameters":[{"name":"businessId","required":false,"in":"query","description":"One business; omitted = every business (platform totals)","schema":{"type":"string"}},{"name":"from","required":true,"in":"query","schema":{"example":"2026-09-01","type":"string"}},{"name":"to","required":true,"in":"query","schema":{"example":"2026-09-30","type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Usage meters per business and day (operators); omit businessId for every business","tags":["admin"]}},"/api/v1/admin/audit":{"get":{"operationId":"AdminActionsController_auditLog","parameters":[{"name":"businessId","required":false,"in":"query","description":"Rows about one business","schema":{"type":"string"}},{"name":"actorId","required":false,"in":"query","description":"Operator user id (TICKET-147)","schema":{"type":"string"}},{"name":"action","required":false,"in":"query","description":"Action prefix, e.g. `review.` (TICKET-147)","schema":{"type":"string"}},{"name":"targetType","required":false,"in":"query","schema":{"example":"review","type":"string"}},{"name":"targetId","required":false,"in":"query","schema":{"type":"string"}},{"name":"from","required":false,"in":"query","description":"ISO instant (inclusive)","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","description":"ISO instant (inclusive)","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Admin audit log, newest first (operators)","tags":["admin"]}},"/api/v1/admin/vertical-profiles":{"get":{"operationId":"AdminVerticalsController_list","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/VerticalProfileDto"}}}}}},"security":[{"bearer":[]}],"summary":"Every vertical profile, disabled ones included (operators)","tags":["admin"]},"post":{"operationId":"AdminVerticalsController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateVerticalProfileDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerticalProfileDto"}}}}},"security":[{"bearer":[]}],"summary":"Add a vertical (OPERATOR_ADMIN, audited)","tags":["admin"]}},"/api/v1/admin/vertical-profiles/{key}":{"patch":{"operationId":"AdminVerticalsController_patch","parameters":[{"name":"key","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchVerticalProfileDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerticalProfileDto"}}}}},"security":[{"bearer":[]}],"summary":"Edit a vertical: labels, fields, default policies, filters, tone notes, enabled (OPERATOR_ADMIN, audited)","tags":["admin"]}},"/api/v1/admin/finance/summary":{"get":{"operationId":"AdminFinanceController_summary","parameters":[{"name":"from","required":false,"in":"query","description":"default: the 1st of this month","schema":{"example":"2026-09-01","type":"string"}},{"name":"to","required":false,"in":"query","description":"default: today (inclusive)","schema":{"example":"2026-09-30","type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Platform revenue, GMV, COGS and margin for a date range (sandbox excluded)","tags":["admin"]}},"/api/v1/admin/finance/businesses":{"get":{"operationId":"AdminFinanceController_businesses","parameters":[{"name":"from","required":false,"in":"query","schema":{"type":"string"}},{"name":"to","required":false,"in":"query","schema":{"type":"string"}},{"name":"sort","required":false,"in":"query","schema":{"default":"margin","type":"string","enum":["margin","cogs","revenue","replies"]}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Per-business revenue, COGS and margin, sortable (worst margin first)","tags":["admin"]}},"/api/v1/public/businesses/{slug}":{"get":{"operationId":"PublicListingController_business","parameters":[{"name":"slug","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"404":{"description":"NOT_FOUND (unknown, inactive or hidden)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"summary":"Public profile, catalog, hours and delivery areas (cached 30 s)","tags":["public"]}},"/api/v1/public/businesses/{slug}/availability":{"get":{"operationId":"PublicListingController_availabilityFor","parameters":[{"name":"slug","required":true,"in":"path","schema":{"type":"string"}},{"name":"from","required":true,"in":"query","description":"Inclusive local date (Africa/Accra)","schema":{"example":"2026-09-14","type":"string"}},{"name":"to","required":true,"in":"query","description":"Inclusive; at most 92 days after from","schema":{"example":"2026-09-20","type":"string"}},{"name":"unitId","required":false,"in":"query","description":"Restrict to one unit","schema":{"example":"ckunit1","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvailabilityResponseDto"}}}}},"summary":"Public day calendar for a listed rental","tags":["public"]}},"/api/v1/public/businesses/{slug}/slots":{"get":{"operationId":"PublicListingController_slotsFor","parameters":[{"name":"slug","required":true,"in":"path","schema":{"type":"string"}},{"name":"serviceId","required":true,"in":"query","schema":{"example":"cksvc1","type":"string"}},{"name":"date","required":true,"in":"query","description":"Local date (Africa/Accra)","schema":{"example":"2026-09-15","type":"string"}},{"name":"providerId","required":false,"in":"query","description":"Restrict to one provider","schema":{"example":"ckprov1","type":"string"}},{"name":"stepMin","required":false,"in":"query","description":"Slot grid in minutes","schema":{"example":15,"type":"number","enum":[5,10,15,30,60]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlotsResponseDto"}}}}},"summary":"Public slots for a listed service business","tags":["public"]}},"/api/v1/public/businesses/{slug}/quotes":{"post":{"operationId":"PublicListingController_quoteFor","parameters":[{"name":"slug","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteRequestDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteResponseDto"}}}}},"summary":"Public quote for a listed business (stateless)","tags":["public"]}},"/api/v1/public/businesses/{slug}/holds":{"post":{"operationId":"PublicListingController_holdFor","parameters":[{"name":"slug","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicHoldRequestDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Hold for the signed-in customer (Epic 11, F50): the customer is the session's phone, source APP; keys 403","tags":["public"]}},"/api/v1/public/businesses/{slug}/refs":{"post":{"operationId":"PublicListingController_createRef","parameters":[{"name":"slug","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRefDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefResponseDto"}}}},"404":{"description":"no instant replies on this business","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"summary":"A 24-hour reference for \"Ask Chale on WhatsApp\" (F78) — the viewed context, never the customer","tags":["public"]}},"/api/v1/public/search":{"get":{"operationId":"PublicSearchController_find","parameters":[{"name":"lat","required":false,"in":"query","schema":{"example":5.636,"type":"number"}},{"name":"lng","required":false,"in":"query","schema":{"example":-0.161,"type":"number"}},{"name":"areaId","required":false,"in":"query","description":"origin = the area centroid","schema":{"example":"ckarea1","type":"string"}},{"name":"family","required":false,"in":"query","schema":{"type":"string","enum":["RENTALS","BEAUTY"]}},{"name":"q","required":false,"in":"query","description":"name or an active unit/service name, case-insensitive","schema":{"type":"string"}},{"name":"serviceLocation","required":false,"in":"query","schema":{"type":"string","enum":["AT_BUSINESS","AT_CUSTOMER","BOTH"]}},{"name":"availableOn","required":false,"in":"query","description":"keep businesses with a free unit/slot that day","schema":{"example":"2026-09-19","type":"string"}},{"name":"maxPricePesewas","required":false,"in":"query","schema":{"example":50000,"type":"number"}},{"name":"minRating","required":false,"in":"query","schema":{"example":4,"type":"number"}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","description":"≤ 50","schema":{"example":20,"type":"number"}}],"responses":{"200":{"description":"{ data: SearchRow[], meta: { nextCursor, hasMore, limit, origin } }"}},"summary":"Listed businesses nearest-first (GPS point or area), with filters (F47, F81, F82, F85)","tags":["public"]}},"/api/v1/public/refs/{ref}":{"get":{"operationId":"PublicSearchController_resolve","parameters":[{"name":"ref","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"404":{"description":"unknown or expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"summary":"Resolve an app reference (C-XXXX) to the context it was made from (F78)","tags":["public"]}},"/api/v1/businesses/{businessId}/availability":{"get":{"operationId":"AvailabilityController_unitAvailability","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"from","required":true,"in":"query","description":"Inclusive local date (Africa/Accra)","schema":{"example":"2026-09-14","type":"string"}},{"name":"to","required":true,"in":"query","description":"Inclusive; at most 92 days after from","schema":{"example":"2026-09-20","type":"string"}},{"name":"unitId","required":false,"in":"query","description":"Restrict to one unit","schema":{"example":"ckunit1","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvailabilityResponseDto"}}}},"403":{"description":"ROLE_REQUIRED · SCOPE_MISSING","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"NOT_FOUND (also for another tenant or unknown unit)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"VALIDATION_FAILED · POLICY_VIOLATION (wrong shape)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Per-unit day calendar for a range of at most 92 days","tags":["availability"]}},"/api/v1/businesses/{businessId}/slots":{"get":{"operationId":"AvailabilityController_serviceSlots","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"serviceId","required":true,"in":"query","schema":{"example":"cksvc1","type":"string"}},{"name":"date","required":true,"in":"query","description":"Local date (Africa/Accra)","schema":{"example":"2026-09-15","type":"string"}},{"name":"providerId","required":false,"in":"query","description":"Restrict to one provider","schema":{"example":"ckprov1","type":"string"}},{"name":"stepMin","required":false,"in":"query","description":"Slot grid in minutes","schema":{"example":15,"type":"number","enum":[5,10,15,30,60]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlotsResponseDto"}}}},"403":{"description":"ROLE_REQUIRED · SCOPE_MISSING","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"NOT_FOUND (unknown service or provider, or another tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"VALIDATION_FAILED · POLICY_VIOLATION (wrong shape)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Bookable slots per provider for one service on one date","tags":["availability"]}},"/api/v1/businesses/{businessId}/quotes":{"post":{"operationId":"PricingController_quote","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteRequestDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuoteResponseDto"}}}},"403":{"description":"ROLE_REQUIRED · SCOPE_MISSING","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"NOT_FOUND (unknown resource, or another tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"VALIDATION_FAILED · POLICY_VIOLATION (wrong shape)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Price a unit day or a service slot, with delivery and extras","tags":["pricing"]}},"/api/v1/businesses/{businessId}/holds":{"post":{"description":"Quotes, then locks the resource-day and re-reads the calendar inside the transaction; the HELD booking expires automatically. Replay the same Idempotency-Key to get the same booking back.","operationId":"HoldsController_create","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","description":"8–128 chars [A-Za-z0-9_-]","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldRequestDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldResponseDto"}}}},"400":{"description":"missing Idempotency-Key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"403":{"description":"ROLE_REQUIRED · SCOPE_MISSING","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"NOT_FOUND (also another tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"NOT_AVAILABLE — taken, blocked or closed · IDEMPOTENCY_MISMATCH","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"VALIDATION_FAILED (incl. uncovered delivery area) · POLICY_VIOLATION (wrong shape)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Hold a unit/date or a service slot for the policy TTL (concurrency-safe)","tags":["bookings"]}},"/api/v1/businesses/{businessId}/subscription":{"get":{"operationId":"BillingController_subscription","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Plan, status, period, open invoice (STAFF and keys see plan/status/readOnly only)","tags":["billing"]}},"/api/v1/businesses/{businessId}/subscription/checkout":{"post":{"operationId":"BillingController_checkout","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Payment link for the next period of a plan (OWNER)","tags":["billing"]}},"/api/v1/businesses/{businessId}/subscription/cancel":{"post":{"operationId":"BillingController_cancel","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Cancel the subscription (OWNER)","tags":["billing"]}},"/api/v1/businesses/{businessId}/invoices":{"get":{"operationId":"BillingController_invoices","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"object"}}}}},"security":[{"bearer":[]}],"summary":"Invoices, newest first (OWNER)","tags":["billing"]}},"/api/v1/businesses/{businessId}/customers":{"get":{"operationId":"CustomersController_list","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"q","required":false,"in":"query","description":"Case-insensitive prefix on name or phone","schema":{"example":"ama","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerListResponseDto"}}}},"403":{"description":"ROLE_REQUIRED · SCOPE_MISSING","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"NOT_FOUND (also for another tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Customers of the business, cursor-paginated, optional name/phone prefix","tags":["customers"]}},"/api/v1/customers/{customerId}":{"get":{"operationId":"CustomersController_get","parameters":[{"name":"customerId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerResponseDto"}}}},"404":{"description":"NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"One customer (tenant derived from the row; 404 for other tenants)","tags":["customers"]}},"/api/v1/businesses/{businessId}/digest/today":{"get":{"operationId":"DigestController_today","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DigestResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Today's live bookings with counts and money outstanding (Africa/Accra day)","tags":["bookings"]}},"/api/v1/businesses/{businessId}/units":{"get":{"operationId":"UnitsController_list","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"active","required":false,"in":"query","description":"Only active units","schema":{"example":true,"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnitListResponseDto"}}}},"404":{"description":"NOT_FOUND (also for another tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Units of the business (optionally only active ones)","tags":["catalog"]},"post":{"operationId":"UnitsController_create","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUnitDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnitResponseDto"}}}},"403":{"description":"ROLE_REQUIRED · SCOPE_MISSING","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"VALIDATION_FAILED · POLICY_VIOLATION (wrong shape)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Add a unit (UNIT_PER_DAY businesses only)","tags":["catalog"]}},"/api/v1/businesses/{businessId}/units/{unitId}":{"patch":{"operationId":"UnitsController_update","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"unitId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUnitDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnitResponseDto"}}}},"404":{"description":"NOT_FOUND (also for another tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Update a unit","tags":["catalog"]},"delete":{"operationId":"UnitsController_remove","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"unitId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted"},"404":{"description":"NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Soft-delete a unit (kept for existing bookings, hidden from availability)","tags":["catalog"]}},"/api/v1/businesses/{businessId}/services":{"get":{"operationId":"ServicesController_listServices","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"active","required":false,"in":"query","schema":{"example":true,"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceListResponseDto"}}}},"404":{"description":"NOT_FOUND (also for another tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Services of the business","tags":["catalog"]},"post":{"operationId":"ServicesController_createService","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateServiceDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceResponseDto"}}}},"403":{"description":"ROLE_REQUIRED · SCOPE_MISSING","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"VALIDATION_FAILED · POLICY_VIOLATION (wrong shape)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Add a service (SERVICE_IN_SLOT businesses only)","tags":["catalog"]}},"/api/v1/businesses/{businessId}/services/{serviceId}":{"patch":{"operationId":"ServicesController_updateService","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"serviceId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateServiceDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceResponseDto"}}}},"404":{"description":"NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Update a service","tags":["catalog"]},"delete":{"operationId":"ServicesController_removeService","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"serviceId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted"},"404":{"description":"NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Soft-delete a service and unlink it from every provider","tags":["catalog"]}},"/api/v1/businesses/{businessId}/providers":{"get":{"operationId":"ServicesController_listProviders","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"active","required":false,"in":"query","schema":{"example":true,"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderListResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Providers with their services and weekly schedule","tags":["catalog"]},"post":{"operationId":"ServicesController_createProvider","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProviderDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderResponseDto"}}}},"422":{"description":"VALIDATION_FAILED (userId not a member) · POLICY_VIOLATION","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Add a provider (SERVICE_IN_SLOT businesses only)","tags":["catalog"]}},"/api/v1/businesses/{businessId}/providers/me":{"get":{"operationId":"ServicesController_myProvider","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderResponseDto"}}}},"403":{"description":"FORBIDDEN (API keys have no user)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"no provider linked to this member","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"The provider linked to the caller's user (STAFF 'My day', TICKET-120) — sessions only","tags":["catalog"]}},"/api/v1/businesses/{businessId}/providers/{providerId}":{"patch":{"operationId":"ServicesController_updateProvider","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"providerId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProviderDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderResponseDto"}}}},"404":{"description":"NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Update a provider","tags":["catalog"]},"delete":{"operationId":"ServicesController_removeProvider","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"providerId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted"},"404":{"description":"NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Soft-delete a provider","tags":["catalog"]}},"/api/v1/businesses/{businessId}/providers/{providerId}/services":{"put":{"operationId":"ServicesController_setProviderServices","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"providerId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetProviderServicesDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderResponseDto"}}}},"422":{"description":"VALIDATION_FAILED (unknown service)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Replace the services this provider performs","tags":["catalog"]}},"/api/v1/businesses/{businessId}/providers/{providerId}/schedule":{"put":{"operationId":"ServicesController_setProviderSchedule","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"providerId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PutProviderScheduleDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProviderResponseDto"}}}},"422":{"description":"VALIDATION_FAILED (times, breaks)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Replace the weekly schedule (days not listed are days off)","tags":["catalog"]}},"/api/v1/businesses/{businessId}/extras":{"get":{"operationId":"ExtrasController_listExtras","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"active","required":false,"in":"query","schema":{"example":true,"type":"boolean"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExtraListResponseDto"}}}},"404":{"description":"NOT_FOUND (also for another tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Add-ons offered by the business","tags":["catalog"]},"post":{"operationId":"ExtrasController_createExtra","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateExtraDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExtraResponseDto"}}}},"403":{"description":"ROLE_REQUIRED · SCOPE_MISSING","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Add an extra","tags":["catalog"]}},"/api/v1/businesses/{businessId}/extras/{extraId}":{"patch":{"operationId":"ExtrasController_updateExtra","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"extraId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateExtraDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExtraResponseDto"}}}},"404":{"description":"NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Update an extra","tags":["catalog"]},"delete":{"operationId":"ExtrasController_removeExtra","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"extraId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Deleted"},"404":{"description":"NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Soft-delete an extra","tags":["catalog"]}},"/api/v1/businesses/{businessId}/blocks":{"get":{"operationId":"ExtrasController_listBlocks","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"from","required":false,"in":"query","description":"Only blocks ending at or after this instant","schema":{"example":"2026-09-01T00:00:00.000Z","type":"string"}},{"name":"to","required":false,"in":"query","description":"Only blocks starting at or before this instant","schema":{"example":"2026-10-01T00:00:00.000Z","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlockListResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Blocks, optionally within a window (?from=&to=)","tags":["catalog"]},"post":{"operationId":"ExtrasController_createBlock","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBlockDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlockResponseDto"}}}},"422":{"description":"VALIDATION_FAILED (window, unknown unit/provider, both targets)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Block a unit, a provider, or the whole business for a window (overlaps allowed)","tags":["catalog"]}},"/api/v1/businesses/{businessId}/blocks/{blockId}":{"delete":{"operationId":"ExtrasController_removeBlock","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"blockId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":"Removed"},"404":{"description":"NOT_FOUND (also for another tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Remove a block","tags":["catalog"]}},"/api/v1/businesses/{businessId}/bookings":{"get":{"operationId":"TenantBookingsController_list","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"from","required":false,"in":"query","description":"Bookings ending on/after this local date","schema":{"example":"2026-09-01","type":"string"}},{"name":"to","required":false,"in":"query","description":"Bookings starting on/before this local date","schema":{"example":"2026-09-30","type":"string"}},{"name":"status","required":false,"in":"query","schema":{"type":"string","enum":["HELD","CONFIRMED","CHECKED_IN","COMPLETED","CANCELLED_BY_CUSTOMER","CANCELLED_BY_BUSINESS","NO_SHOW","EXPIRED"]}},{"name":"unitId","required":false,"in":"query","schema":{"example":"ckunit1","type":"string"}},{"name":"providerId","required":false,"in":"query","schema":{"example":"ckprov1","type":"string"}},{"name":"customerId","required":false,"in":"query","schema":{"example":"ckcust1","type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingListResponseDto"}}}},"403":{"description":"ROLE_REQUIRED · SCOPE_MISSING","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"NOT_FOUND (also another tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Bookings of the business in time order, cursor-paginated","tags":["bookings"]},"post":{"description":"Same body as a hold; the source defaults to MANUAL. Availability is checked under the same lock.","operationId":"TenantBookingsController_create","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManualBookingDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldResponseDto"}}}},"409":{"description":"NOT_AVAILABLE","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Manual booking (owner/staff or key): CONFIRMED at once, or HELD with confirm=false","tags":["bookings"]}},"/api/v1/bookings/{bookingId}":{"get":{"operationId":"BookingsController_get","parameters":[{"name":"bookingId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingResponseDto"}}}},"404":{"description":"NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"One booking (members, the business key, or the customer it was made for)","tags":["bookings"]}},"/api/v1/bookings/{bookingId}/confirm":{"post":{"operationId":"BookingsController_confirm","parameters":[{"name":"bookingId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionNoteDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingResponseDto"}}}},"404":{"description":"NOT_FOUND (also another tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"ILLEGAL_TRANSITION","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Confirm a hold (deposit collected offline or waived)","tags":["bookings"]}},"/api/v1/bookings/{bookingId}/check-in":{"post":{"operationId":"BookingsController_checkIn","parameters":[{"name":"bookingId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionNoteDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingResponseDto"}}}},"409":{"description":"ILLEGAL_TRANSITION","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Check in (customer arrived / crew departed)","tags":["bookings"]}},"/api/v1/bookings/{bookingId}/complete":{"post":{"operationId":"BookingsController_complete","parameters":[{"name":"bookingId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionNoteDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingResponseDto"}}}},"409":{"description":"ILLEGAL_TRANSITION","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Complete (from CONFIRMED or CHECKED_IN)","tags":["bookings"]}},"/api/v1/bookings/{bookingId}/no-show":{"post":{"operationId":"BookingsController_noShow","parameters":[{"name":"bookingId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActionNoteDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingResponseDto"}}}},"409":{"description":"ILLEGAL_TRANSITION","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"No-show","tags":["bookings"]}},"/api/v1/bookings/{bookingId}/reschedule":{"post":{"operationId":"BookingsController_reschedule","parameters":[{"name":"bookingId","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RescheduleDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RescheduleResponseDto"}}}},"404":{"description":"NOT_FOUND (also another tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"NOT_AVAILABLE · ILLEGAL_TRANSITION","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Move a HELD/CONFIRMED booking to another day or slot (same lock as a hold; the policy decides free)","tags":["bookings"]}},"/api/v1/bookings/{bookingId}/cancel":{"post":{"operationId":"BookingsController_cancel","parameters":[{"name":"bookingId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelBookingDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelResponseDto"}}}},"403":{"description":"FORBIDDEN (customer cancelling as BUSINESS)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"ILLEGAL_TRANSITION","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Cancel with the policy outcome (REFERENCE §6 rule 5); customers may only cancel their own booking","tags":["bookings"]}},"/api/v1/me/bookings":{"get":{"operationId":"MeBookingsController_list","parameters":[{"name":"scope","required":false,"in":"query","schema":{"default":"upcoming","type":"string","enum":["upcoming","past"]}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingListResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Bookings made under the session's phone, across businesses (upcoming ascending, past descending)","tags":["me"]}},"/api/v1/bookings/{bookingId}/payments":{"post":{"operationId":"PaymentsController_record","parameters":[{"name":"bookingId","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordPaymentDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentResponseDto"}}}},"404":{"description":"NOT_FOUND (also another tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"ILLEGAL_TRANSITION (cancelled/expired booking)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"amount above the outstanding balance","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Record a cash / direct-MoMo payment; a covering deposit confirms a HELD booking","tags":["payments"]},"get":{"operationId":"PaymentsController_list","parameters":[{"name":"bookingId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentListResponseDto"}}}},"404":{"description":"NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Payments recorded on a booking","tags":["payments"]}},"/api/v1/bookings/{bookingId}/payments/deposit":{"post":{"operationId":"PaymentsController_depositLink","parameters":[{"name":"bookingId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentLinkResponseDto"}}}},"422":{"description":"deposit already covered","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Paystack link for the deposit still due (TICKET-046, F27)","tags":["payments"]}},"/api/v1/bookings/{bookingId}/payments/balance":{"post":{"operationId":"PaymentsController_balanceLink","parameters":[{"name":"bookingId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentLinkResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Paystack link for the balance still due (TICKET-046, F27)","tags":["payments"]}},"/api/v1/payments/{paymentId}/refund":{"post":{"operationId":"PaymentsController_refund","parameters":[{"name":"paymentId","required":true,"in":"path","schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundPaymentDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentResponseDto"}}}},"403":{"description":"ROLE_REQUIRED (staff)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"amount above what is left","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Record a refund (owner); the money itself moves off-platform until Paystack lands","tags":["payments"]}},"/api/v1/businesses/{businessId}/finances/summary":{"get":{"operationId":"FinanceController_summary","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"from","required":false,"in":"query","schema":{"example":"2026-09-01","type":"string"}},{"name":"to","required":false,"in":"query","schema":{"example":"2026-09-30","type":"string"}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FinanceSummaryDto"}}}}},"security":[{"bearer":[]}],"summary":"Deposits, collected by method, balances due, payouts, subscription (OWNER)","tags":["finances"]}},"/api/v1/businesses/{businessId}/payments":{"get":{"operationId":"FinanceController_payments","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"from","required":false,"in":"query","schema":{"example":"2026-09-01","type":"string"}},{"name":"to","required":false,"in":"query","schema":{"example":"2026-09-30","type":"string"}},{"name":"cursor","required":false,"in":"query","schema":{"type":"string"}},{"name":"limit","required":false,"in":"query","schema":{"type":"number"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Money rows of the business, newest first (OWNER)","tags":["finances"]}},"/api/v1/businesses/{businessId}/payouts":{"get":{"operationId":"FinanceController_payouts","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayoutDto"}}}}},"security":[{"bearer":[]}],"summary":"Where deposits settle (Paystack subaccount), masked (OWNER)","tags":["finances"]}},"/api/v1/businesses/{businessId}/payouts/onboard":{"post":{"operationId":"FinanceController_onboard","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayoutOnboardDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayoutDto"}}}}},"security":[{"bearer":[]}],"summary":"Create/replace the Paystack subaccount for settlements (OWNER, F28)","tags":["finances"]}},"/api/v1/webhooks/paystack":{"post":{"operationId":"PaystackWebhookController_paystack","parameters":[],"responses":{"200":{"description":""}},"summary":"Paystack events (charge.success → payment PAID / invoice PAID)","tags":["webhooks"]}},"/api/v1/businesses/{businessId}/webhooks":{"post":{"operationId":"TenantWebhooksController_create","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResponseDto"}}}},"403":{"description":"ROLE_REQUIRED · SCOPE_MISSING","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"404":{"description":"NOT_FOUND (another tenant)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"422":{"description":"url not https / not public","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Subscribe a URL to events; the signing secret is returned once","tags":["webhooks"]},"get":{"operationId":"TenantWebhooksController_list","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookListResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Endpoints of the business (secrets never shown again)","tags":["webhooks"]}},"/api/v1/webhooks/{webhookId}":{"patch":{"operationId":"WebhooksController_update","parameters":[{"name":"webhookId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResponseDto"}}}},"404":{"description":"NOT_FOUND","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Change url, events, description or enabled","tags":["webhooks"]},"delete":{"operationId":"WebhooksController_remove","parameters":[{"name":"webhookId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Remove an endpoint (pending deliveries die)","tags":["webhooks"]}},"/api/v1/webhooks/{webhookId}/rotate":{"post":{"operationId":"WebhooksController_rotate","parameters":[{"name":"webhookId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Rotate the signing secret (returned once)","tags":["webhooks"]}},"/api/v1/webhooks/{webhookId}/test":{"post":{"operationId":"WebhooksController_test","parameters":[{"name":"webhookId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Queue a signed `ping` delivery","tags":["webhooks"]}},"/api/v1/webhooks/{webhookId}/deliveries":{"get":{"operationId":"WebhooksController_deliveries","parameters":[{"name":"webhookId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeliveryListResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Delivery log of an endpoint, cursor-paginated","tags":["webhooks"]}},"/api/v1/deliveries/{deliveryId}/replay":{"post":{"operationId":"WebhooksController_replay","parameters":[{"name":"deliveryId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Attempt a delivery again now (also for DEAD ones)","tags":["webhooks"]}},"/api/v1/businesses/{businessId}/usage":{"post":{"operationId":"MeteringController_add","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageReportDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Add usage counters for a day (assistant flush; additive)","tags":["internal"]},"get":{"operationId":"MeteringController_day","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"A day's counters (owner, or the assistant key)","tags":["internal"]}},"/api/v1/me/devices":{"get":{"operationId":"NotificationsController_devices","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceListResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"The caller's registered devices (never the tokens)","tags":["me"]},"post":{"operationId":"NotificationsController_register","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterDeviceDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Register (or refresh) this device for push","tags":["me"]}},"/api/v1/me/devices/{deviceId}":{"delete":{"operationId":"NotificationsController_remove","parameters":[{"name":"deviceId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Forget a device (sign-out)","tags":["me"]}},"/api/v1/me/notifications":{"get":{"operationId":"NotificationsController_notifications","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotificationListResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"The caller's notifications, newest first","tags":["me"]}},"/api/v1/businesses/{businessId}/uploads":{"post":{"operationId":"UploadsController_create","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUploadDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UploadResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Pre-signed PUT URL for a photo (≤ 5 MB)","tags":["uploads"]}},"/api/v1/businesses/{businessId}/verification":{"post":{"operationId":"VerificationController_submit","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitVerificationDto"}}}},"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Submit ID, registration and premises photos for MoyoLab to verify (OWNER)","tags":["verification"]},"get":{"operationId":"VerificationController_latest","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"The latest verification request and its outcome (no document links)","tags":["verification"]}},"/api/v1/admin/verification":{"get":{"operationId":"AdminVerificationController_queue","parameters":[{"name":"status","required":false,"in":"query","schema":{"default":"SUBMITTED","type":"string","enum":["SUBMITTED","APPROVED","REJECTED"]}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Verification queue (SUBMITTED oldest first by default)","tags":["admin"]}},"/api/v1/admin/verification/{id}":{"get":{"operationId":"AdminVerificationController_detail","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"One request with 15-minute document links (audited)","tags":["admin"]}},"/api/v1/admin/verification/{id}/approve":{"post":{"operationId":"AdminVerificationController_approve","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationDecisionDto"}}}},"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Approve: the business goes ACTIVE (publishing gate applies) — audited","tags":["admin"]}},"/api/v1/admin/verification/{id}/reject":{"post":{"operationId":"AdminVerificationController_reject","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationRejectDto"}}}},"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Reject with a reason the owner reads; the business returns to DRAFT — audited","tags":["admin"]}},"/api/v1/bookings/{bookingId}/disputes":{"post":{"operationId":"DisputesController_open","parameters":[{"name":"bookingId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenDisputeDto"}}}},"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Report a problem with a booking (the booking's customer or the business owner)","tags":["disputes"]},"get":{"operationId":"DisputesController_list","parameters":[{"name":"bookingId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"A booking's disputes (same readers as the booking)","tags":["disputes"]}},"/api/v1/admin/disputes":{"get":{"operationId":"AdminDisputesController_list","parameters":[{"name":"status","required":false,"in":"query","schema":{"default":"OPEN","type":"string","enum":["OPEN","RESOLVED"]}},{"name":"businessId","required":false,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Disputes (OPEN oldest first by default) with booking, payments, business","tags":["admin"]},"post":{"operationId":"AdminDisputesController_open","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminOpenDisputeDto"}}}},"responses":{"201":{"description":""}},"security":[{"bearer":[]}],"summary":"Open a dispute on a booking on behalf of a caller — audited","tags":["admin"]}},"/api/v1/admin/disputes/{disputeId}/resolve":{"post":{"operationId":"AdminDisputesController_resolve","parameters":[{"name":"disputeId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolveDisputeDto"}}}},"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Resolve: full / partial / no refund (PSP for Paystack rows) — audited, both sides pushed","tags":["admin"]}},"/api/v1/admin/payments":{"get":{"operationId":"AdminPaymentsController_list","parameters":[{"name":"businessId","required":false,"in":"query","schema":{"type":"string"}},{"name":"status","required":false,"in":"query","schema":{"type":"string","enum":["PENDING","PAID","FAILED","REFUNDED"]}},{"name":"method","required":false,"in":"query","schema":{"type":"string","enum":["CASH","MOMO_DIRECT","PAYSTACK"]}},{"name":"kind","required":false,"in":"query","schema":{"type":"string","enum":["DEPOSIT","BALANCE"]}},{"name":"from","required":false,"in":"query","schema":{"example":"2026-09-01","type":"string"}},{"name":"to","required":false,"in":"query","description":"exclusive","schema":{"example":"2026-09-30","type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Payments across businesses, newest first (sandbox excluded)","tags":["admin"]}},"/api/v1/admin/payments/{paymentId}/refund":{"post":{"operationId":"AdminPaymentsController_refund","parameters":[{"name":"paymentId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminRefundDto"}}}},"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Refund a payment without a dispute (PSP for Paystack rows) — audited","tags":["admin"]}},"/api/v1/feature-flags":{"get":{"operationId":"FeatureFlagsController_evaluate","parameters":[{"name":"businessId","required":false,"in":"query","description":"Evaluate for one of your businesses (keys: ignored, their own)","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"The flags as they apply to you (a key: its business; a session: a business you belong to, else the global values)","tags":["feature-flags"]}},"/api/v1/admin/feature-flags":{"get":{"operationId":"AdminFeatureFlagsController_list","parameters":[],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Every flag with its global value and allow-list","tags":["admin"]}},"/api/v1/admin/feature-flags/{key}":{"put":{"operationId":"AdminFeatureFlagsController_upsert","parameters":[{"name":"key","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertFeatureFlagDto"}}}},"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Create or change a flag — audited, caches dropped","tags":["admin"]},"delete":{"operationId":"AdminFeatureFlagsController_remove","parameters":[{"name":"key","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Remove a flag — audited","tags":["admin"]}},"/api/v1/businesses/{businessId}/events/handoff":{"post":{"operationId":"HandoffEventsController_handoff","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HandoffEventDto"}}}},"responses":{"202":{"description":""}},"security":[{"bearer":[]}],"summary":"Emit handoff.requested to owner realtime + webhooks (assistant)","tags":["internal"]}},"/api/v1/bookings/{bookingId}/reviews":{"post":{"operationId":"BookingReviewsController_create","parameters":[{"name":"bookingId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateReviewDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewResponseDto"}}}},"404":{"description":"not this customer's booking","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}},"409":{"description":"ALREADY_EXISTS · ILLEGAL_TRANSITION","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Review a completed booking (the customer it was made for, once)","tags":["reviews"]}},"/api/v1/reviews/{reviewId}/reply":{"post":{"operationId":"ReviewRepliesController_reply","parameters":[{"name":"reviewId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplyReviewDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"The business's reply (OWNER; sets or edits)","tags":["reviews"]}},"/api/v1/public/businesses/{slug}/reviews":{"get":{"operationId":"PublicReviewsController_list","parameters":[{"name":"slug","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Published reviews of a listed business, newest first","tags":["public"]}},"/api/v1/admin/reviews":{"get":{"operationId":"AdminReviewsController_list","parameters":[{"name":"businessId","required":false,"in":"query","schema":{"type":"string"}},{"name":"status","required":false,"in":"query","schema":{"type":"string","enum":["PUBLISHED","HIDDEN"]}},{"name":"minRating","required":false,"in":"query","schema":{"minimum":1,"maximum":5,"type":"number"}},{"name":"maxRating","required":false,"in":"query","schema":{"minimum":1,"maximum":5,"type":"number"}},{"name":"q","required":false,"in":"query","description":"matches the review text","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Reviews across businesses for moderation (Epic 12, TICKET-143)","tags":["admin"]}},"/api/v1/admin/reviews/{reviewId}/hide":{"post":{"operationId":"AdminReviewsController_hide","parameters":[{"name":"reviewId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HideReviewDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Hide a review with a reason (OPERATOR_ADMIN, audited) — leaves the list and the average","tags":["admin"]}},"/api/v1/admin/reviews/{reviewId}/show":{"post":{"operationId":"AdminReviewsController_show","parameters":[{"name":"reviewId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Show a hidden review again (OPERATOR_ADMIN, audited)","tags":["admin"]}},"/api/v1/sandbox":{"post":{"operationId":"SandboxController_create","parameters":[],"responses":{"201":{"description":"{ data: { business, key } }"}},"security":[{"bearer":[]}],"summary":"Create (201, key shown once) or return (200, no key) my sandbox business","tags":["sandbox"]},"delete":{"operationId":"SandboxController_reset","parameters":[],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Reset my sandbox: activity wiped, catalogue re-seeded; keys and endpoints kept","tags":["sandbox"]}},"/api/v1/businesses/{businessId}/calendar-feeds":{"post":{"operationId":"CalendarFeedsController_create","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCalendarFeedDto"}}}},"responses":{"201":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CalendarFeedDto"}}}}},"security":[{"bearer":[]}],"summary":"Create a feed URL (shown once) for the business or one provider","tags":["calendar"]},"get":{"operationId":"CalendarFeedsController_list","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CalendarFeedDto"}}}}}},"security":[{"bearer":[]}],"summary":"Active feeds (no URLs)","tags":["calendar"]}},"/api/v1/businesses/{businessId}/bookings/export.csv":{"get":{"operationId":"CalendarFeedsController_exportCsv","parameters":[{"name":"businessId","required":true,"in":"path","schema":{"type":"string"}},{"name":"from","required":false,"in":"query","schema":{"example":"2026-09-01","type":"string"}},{"name":"to","required":false,"in":"query","schema":{"example":"2026-09-30","type":"string"}}],"responses":{"200":{"description":""}},"security":[{"bearer":[]}],"summary":"Bookings as CSV (OWNER; range ≤ 366 days, default this month)","tags":["calendar"]}},"/api/v1/calendar-feeds/{feedId}":{"delete":{"operationId":"CalendarFeedRoutesController_revoke","parameters":[{"name":"feedId","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"204":{"description":""}},"security":[{"bearer":[]}],"summary":"Revoke a feed (its URL stops working at once)","tags":["calendar"]}},"/api/v1/feeds/{token}/calendar.ics":{"get":{"operationId":"CalendarFeedRoutesController_feed","parameters":[{"name":"token","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"The iCal feed (public by URL; the token is the credential)","tags":["calendar"]}}},"info":{"title":"Chale API","description":"Public API of the Chale booking platform (docs/API.md conventions).","version":"1","contact":{}},"tags":[],"servers":[{"url":"https://api-staging.chalehq.com/api/v1"}],"components":{"securitySchemes":{"bearer":{"scheme":"bearer","bearerFormat":"JWT or chk_ API key","type":"http"}},"schemas":{"DeviceDto":{"type":"object","properties":{"platform":{"type":"string","enum":["android","ios","web"]},"appVersion":{"type":"string","example":"1.0.0"}},"required":["platform"]},"FirebaseExchangeDto":{"type":"object","properties":{"idToken":{"type":"string","description":"Firebase phone-auth ID token"},"device":{"$ref":"#/components/schemas/DeviceDto"}},"required":["idToken"]},"MembershipDto":{"type":"object","properties":{"businessId":{"type":"string"},"businessName":{"type":"string"},"role":{"type":"string","enum":["OWNER","STAFF"]}},"required":["businessId","businessName","role"]},"AuthUserDto":{"type":"object","properties":{"id":{"type":"string"},"phone":{"type":"string","example":"+233241234567"},"name":{"type":"string","nullable":true},"roles":{"type":"array","items":{"type":"string"}},"memberships":{"type":"array","items":{"$ref":"#/components/schemas/MembershipDto"}}},"required":["id","phone","name","roles","memberships"]},"AuthTokensDto":{"type":"object","properties":{"accessToken":{"type":"string"},"refreshToken":{"type":"string"},"expiresIn":{"type":"number","example":900},"user":{"$ref":"#/components/schemas/AuthUserDto"}},"required":["accessToken","refreshToken","expiresIn","user"]},"AuthResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AuthTokensDto"}},"required":["data"]},"ErrorDetailDto":{"type":"object","properties":{"field":{"type":"string","example":"date"},"issue":{"type":"string","example":"must be ISO 8601"}},"required":["field","issue"]},"ErrorDto":{"type":"object","properties":{"code":{"type":"string","example":"NOT_FOUND"},"message":{"type":"string","example":"Not found."},"details":{"type":"array","items":{"$ref":"#/components/schemas/ErrorDetailDto"}},"requestId":{"type":"string","example":"req_abc123"}},"required":["code","message","requestId"]},"ErrorResponseDto":{"type":"object","properties":{"error":{"$ref":"#/components/schemas/ErrorDto"}},"required":["error"]},"RefreshDto":{"type":"object","properties":{"refreshToken":{"type":"string","example":"rt_…"}},"required":["refreshToken"]},"HealthDto":{"type":"object","properties":{"status":{"type":"string","example":"ok","enum":["ok"]},"version":{"type":"string","description":"Image git sha (APP_VERSION)","example":"a1b2c3d"},"uptimeSeconds":{"type":"number","example":42}},"required":["status","version","uptimeSeconds"]},"HealthResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/HealthDto"}},"required":["data"]},"ReadyChecksDto":{"type":"object","properties":{"db":{"type":"string","example":"ok","enum":["ok"]},"redis":{"type":"string","example":"ok","enum":["ok"]}},"required":["db","redis"]},"ReadyDto":{"type":"object","properties":{"status":{"type":"string","example":"ready","enum":["ready"]},"checks":{"$ref":"#/components/schemas/ReadyChecksDto"}},"required":["status","checks"]},"ReadyResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ReadyDto"}},"required":["data"]},"CreateBusinessDto":{"type":"object","properties":{"name":{"type":"string","example":"Bounce Kingdom"},"verticalProfileKey":{"type":"string","example":"party_rentals","description":"A key from the vertical registry"},"serviceLocation":{"type":"string","enum":["AT_BUSINESS","AT_CUSTOMER","BOTH"],"default":"AT_BUSINESS"},"phone":{"type":"string","example":"+233241234567","description":"E.164"},"description":{"type":"string","example":"Bouncing castles for parties in Accra & Tema"}},"required":["name","verticalProfileKey"]},"DepositPolicyDto":{"type":"object","properties":{"type":{"type":"string","enum":["FIXED","PERCENT"]},"valuePesewas":{"type":"number","description":"Integer pesewas when type is FIXED","example":20000},"percent":{"type":"number","description":"0..100 when type is PERCENT","example":30}},"required":["type"]},"CancellationPolicyDto":{"type":"object","properties":{"freeRescheduleHours":{"type":"number","example":48},"refundableCancelHours":{"type":"number","example":48},"forfeitUnderHours":{"type":"number","example":24}},"required":["freeRescheduleHours","refundableCancelHours","forfeitUnderHours"]},"PoliciesDto":{"type":"object","properties":{"deposit":{"$ref":"#/components/schemas/DepositPolicyDto"},"holdTtlMin":{"type":"number","description":"Hold TTL in minutes, 5..1440","example":120},"halfDayEnabled":{"type":"boolean","example":false},"cancellation":{"$ref":"#/components/schemas/CancellationPolicyDto"}},"required":["deposit","holdTtlMin","halfDayEnabled","cancellation"]},"LatLngDto":{"type":"object","properties":{"lat":{"type":"number","example":5.6698},"lng":{"type":"number","example":-0.0166}},"required":["lat","lng"]},"BusinessDto":{"type":"object","properties":{"id":{"type":"string","example":"ckbiz1"},"slug":{"type":"string","example":"bounce-kingdom"},"name":{"type":"string","example":"Bounce Kingdom"},"description":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"status":{"type":"string","enum":["DRAFT","PENDING_VERIFICATION","ACTIVE","SUSPENDED"]},"verticalProfileKey":{"type":"string","example":"party_rentals"},"family":{"type":"string","enum":["RENTALS","BEAUTY"]},"shape":{"type":"string","enum":["UNIT_PER_DAY","SERVICE_IN_SLOT"]},"serviceLocation":{"type":"string","enum":["AT_BUSINESS","AT_CUSTOMER","BOTH"]},"timezone":{"type":"string","example":"Africa/Accra"},"currency":{"type":"string","example":"GHS"},"listingVisibility":{"type":"string","enum":["PUBLIC","HIDDEN"]},"policies":{"$ref":"#/components/schemas/PoliciesDto"},"location":{"nullable":true,"type":"object","allOf":[{"$ref":"#/components/schemas/LatLngDto"}]},"addressLine":{"type":"string","nullable":true},"areaId":{"type":"string","nullable":true},"depositInstructions":{"type":"string","nullable":true,"description":"F27a deposit instructions (owner-facing; the hold response carries them to the customer)"},"verifiedAt":{"type":"string","nullable":true,"description":"Set by an operator verify (TICKET-072)"},"suspendedReason":{"type":"string","nullable":true},"photos":{"description":"listing photos (TICKET-109)","type":"array","items":{"type":"string"}},"sandbox":{"type":"boolean","description":"a developer sandbox (TICKET-115): simulator payments, never listed"},"ratingAvg":{"type":"number","nullable":true,"description":"Epic 11: from PUBLISHED reviews"},"ratingCount":{"type":"number","example":0},"assistantActive":{"type":"boolean","description":"Epic 11 (F77): the assistant reported itself active on this business"},"whatsappNumber":{"type":"string","nullable":true,"description":"E.164 WhatsApp number (assistant or owner)"},"messageOptIn":{"type":"boolean","description":"Epic 11 (F79): customers may message the business on plain WhatsApp"},"listingHiddenBy":{"type":"string","enum":["OWNER","OPERATOR"],"nullable":true,"description":"Epic 12 (F55): who hid the listing — OPERATOR locks the owner switch until unlocked"},"listingHiddenReason":{"type":"string","nullable":true,"description":"The operator's reason when hidden by MoyoLab"},"createdAt":{"type":"string","example":"2026-09-05T10:00:00.000Z"}},"required":["id","slug","name","description","phone","status","verticalProfileKey","family","shape","serviceLocation","timezone","currency","listingVisibility","policies","location","addressLine","areaId","depositInstructions","verifiedAt","suspendedReason","photos","sandbox","ratingAvg","ratingCount","assistantActive","whatsappNumber","messageOptIn","listingHiddenBy","listingHiddenReason","createdAt"]},"BusinessResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/BusinessDto"}},"required":["data"]},"BusinessListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BusinessDto"}}},"required":["data"]},"CancellationPolicyPatchDto":{"type":"object","properties":{"freeRescheduleHours":{"type":"number","example":48},"refundableCancelHours":{"type":"number","example":48},"forfeitUnderHours":{"type":"number","example":24}}},"PoliciesPatchDto":{"type":"object","properties":{"deposit":{"$ref":"#/components/schemas/DepositPolicyDto"},"holdTtlMin":{"type":"number","example":120},"halfDayEnabled":{"type":"boolean","example":false},"cancellation":{"$ref":"#/components/schemas/CancellationPolicyPatchDto"}}},"UpdateBusinessDto":{"type":"object","properties":{"name":{"type":"string","example":"Bounce Kingdom"},"description":{"type":"string","example":"Bouncing castles for parties in Accra & Tema","nullable":true},"phone":{"type":"string","example":"+233241234567","nullable":true},"serviceLocation":{"type":"string","enum":["AT_BUSINESS","AT_CUSTOMER","BOTH"]},"timezone":{"type":"string","enum":["Africa/Accra"]},"depositInstructions":{"type":"object","example":"MoMo 024 123 4567 (Ama Mensah) — send the booking ref as the reference","nullable":true,"description":"F27a: how the business takes deposits; shown with a hold, never on the public listing. null clears it."},"policies":{"$ref":"#/components/schemas/PoliciesPatchDto"}}},"SetLocationDto":{"type":"object","properties":{"lat":{"type":"number","example":5.6698,"description":"WGS84 latitude, within Ghana"},"lng":{"type":"number","example":-0.0166,"description":"WGS84 longitude, within Ghana"},"addressLine":{"type":"string","example":"Community 25, Tema"},"areaId":{"type":"object","example":"ckarea1","nullable":true,"description":"Curated area; derived from the nearest centroid (≤ 8 km) when omitted or null"}},"required":["lat","lng","addressLine"]},"AssistantStatusDto":{"type":"object","properties":{"active":{"type":"boolean","example":true},"whatsappNumber":{"type":"object","nullable":true,"example":"+233551234567","description":"the connected WhatsApp number"}},"required":["active"]},"UpdateListingDto":{"type":"object","properties":{"listingVisibility":{"type":"string","enum":["PUBLIC","HIDDEN"]},"description":{"type":"object","nullable":true,"maxLength":2000},"photos":{"description":"≤ 10 URLs from POST /businesses/:id/uploads","type":"array","items":{"type":"string"}},"messageOptIn":{"type":"boolean","description":"Epic 11 (F79): show \"Message business\" on the listing"},"whatsappNumber":{"type":"object","nullable":true,"example":"+233241234567","description":"the WhatsApp number customers message (ignored while the assistant reports its own)"}}},"MemberDto":{"type":"object","properties":{"id":{"type":"string","example":"ckmem1"},"userId":{"type":"string","example":"ckusr1"},"phone":{"type":"string","example":"+233201112222"},"name":{"type":"string","nullable":true},"role":{"type":"string","enum":["OWNER","STAFF"]},"status":{"type":"string","enum":["PENDING","ACTIVE"]},"createdAt":{"type":"string","example":"2026-09-06T10:00:00.000Z"}},"required":["id","userId","phone","name","role","status","createdAt"]},"MemberListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MemberDto"}}},"required":["data"]},"InviteMemberDto":{"type":"object","properties":{"phone":{"type":"string","example":"+233201112222","description":"E.164; the invitee signs in with this number"},"role":{"type":"string","enum":["OWNER","STAFF"]},"name":{"type":"string","example":"Ama","description":"Display name, kept if the user has none yet"}},"required":["phone","role"]},"MemberResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/MemberDto"}},"required":["data"]},"HoursEntryDto":{"type":"object","properties":{"weekday":{"type":"number","example":1,"description":"0 = Sunday … 6 = Saturday"},"open":{"type":"string","example":"08:00"},"close":{"type":"string","example":"18:00"},"enabled":{"type":"boolean","example":true}},"required":["weekday","open","close","enabled"]},"HoursListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/HoursEntryDto"}}},"required":["data"]},"PutHoursDto":{"type":"object","properties":{"hours":{"description":"Exactly 7 entries, one per weekday","type":"array","items":{"$ref":"#/components/schemas/HoursEntryDto"}}},"required":["hours"]},"ClosureDto":{"type":"object","properties":{"id":{"type":"string","example":"ckclo1"},"from":{"type":"string","example":"2026-12-24"},"to":{"type":"string","example":"2026-12-26"},"reason":{"type":"string","nullable":true}},"required":["id","from","to","reason"]},"ClosureListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ClosureDto"}}},"required":["data"]},"CreateClosureDto":{"type":"object","properties":{"from":{"type":"string","example":"2026-12-24","description":"Local date (Africa/Accra), inclusive"},"to":{"type":"string","example":"2026-12-26"},"reason":{"type":"string","example":"Christmas"}},"required":["from","to"]},"ClosureResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ClosureDto"}},"required":["data"]},"VerticalFieldDto":{"type":"object","properties":{"name":{"type":"string","example":"capacity"},"required":{"type":"boolean","example":true}},"required":["name","required"]},"VerticalProfileDto":{"type":"object","properties":{"key":{"type":"string","example":"party_rentals"},"family":{"type":"string","enum":["RENTALS","BEAUTY"]},"shape":{"type":"string","enum":["UNIT_PER_DAY","SERVICE_IN_SLOT"]},"labels":{"type":"object","additionalProperties":{"type":"string"},"example":{"unit":"castle","units":"castles","deliver":"delivery & setup"}},"fields":{"type":"array","items":{"$ref":"#/components/schemas/VerticalFieldDto"}},"defaultPolicies":{"$ref":"#/components/schemas/PoliciesDto"},"filters":{"example":["capacity","price"],"type":"array","items":{"type":"string"}},"toneNotes":{"type":"string","nullable":true},"updatedAt":{"type":"string","description":"Present on the operators' list (TICKET-122)"},"enabled":{"type":"boolean","example":true}},"required":["key","family","shape","labels","fields","defaultPolicies","filters","toneNotes","enabled"]},"VerticalProfileListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/VerticalProfileDto"}}},"required":["data"]},"AreaResponseItemDto":{"type":"object","properties":{"id":{"type":"string","example":"ckarea1"},"name":{"type":"string","example":"East Legon"},"slug":{"type":"string","example":"east-legon"},"city":{"type":"string","example":"Accra"},"centroid":{"$ref":"#/components/schemas/LatLngDto"}},"required":["id","name","slug","city","centroid"]},"AreaListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/AreaResponseItemDto"}}},"required":["data"]},"DeliveryZoneDto":{"type":"object","properties":{"areaId":{"type":"string","example":"ckarea9"},"areaName":{"type":"string","example":"Tema Community 25"},"feePesewas":{"type":"number","example":8000},"enabled":{"type":"boolean","example":true}},"required":["areaId","areaName","feePesewas","enabled"]},"DeliveryZoneListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DeliveryZoneDto"}}},"required":["data"]},"DeliveryZoneInputDto":{"type":"object","properties":{"areaId":{"type":"string","example":"ckarea9"},"feePesewas":{"type":"number","example":8000,"description":"Integer pesewas"},"enabled":{"type":"boolean","default":true}},"required":["areaId","feePesewas"]},"PutDeliveryZonesDto":{"type":"object","properties":{"zones":{"description":"The full set: zones not listed are removed","type":"array","items":{"$ref":"#/components/schemas/DeliveryZoneInputDto"}}},"required":["zones"]},"CreateApiKeyDto":{"type":"object","properties":{"name":{"type":"string","example":"Chale assistant"},"scopes":{"type":"array","example":["bookings:read","availability:read"],"items":{"type":"string","enum":["bookings:read","bookings:write","availability:read","customers:read","catalog:read","catalog:write","business:write","webhooks:manage","usage:write"]}}},"required":["name","scopes"]},"ApiKeyDto":{"type":"object","properties":{"id":{"type":"string","example":"ckkey1"},"name":{"type":"string","example":"Chale assistant"},"prefix":{"type":"string","example":"a1b2c3d4","description":"Public part of the key; identifies it in logs"},"scopes":{"type":"array","items":{"type":"string","enum":["bookings:read","bookings:write","availability:read","customers:read","catalog:read","catalog:write","business:write","webhooks:manage","usage:write"]}},"lastUsedAt":{"type":"string","nullable":true},"revokedAt":{"type":"string","nullable":true},"createdAt":{"type":"string","example":"2026-09-06T10:00:00.000Z"},"key":{"type":"string","example":"chk_live_a1b2c3d4_…","description":"Plaintext key — shown once, never stored"}},"required":["id","name","prefix","scopes","lastUsedAt","revokedAt","createdAt"]},"ApiKeyResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ApiKeyDto"}},"required":["data"]},"ApiKeyListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeyDto"}}},"required":["data"]},"AdminPageMetaDto":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"},"limit":{"type":"number","example":20}},"required":["nextCursor","hasMore","limit"]},"AdminBusinessListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BusinessDto"}},"meta":{"$ref":"#/components/schemas/AdminPageMetaDto"}},"required":["data","meta"]},"AdminStatusDto":{"type":"object","properties":{"status":{"type":"string","enum":["ACTIVE","PENDING_VERIFICATION","SUSPENDED"]},"reason":{"type":"string","description":"Required when suspending","example":"Fake photos, no reply to verification call"}},"required":["status"]},"AdminListingDto":{"type":"object","properties":{"listingVisibility":{"type":"string","enum":["PUBLIC","HIDDEN"]},"reason":{"type":"string","example":"Photos show a different business","description":"Required when hiding (the owner reads it, TICKET-142) unless `unlock` is set"},"unlock":{"type":"boolean","description":"With HIDDEN: keep it hidden but hand the switch back to the owner (TICKET-142)"}},"required":["listingVisibility"]},"VerticalFieldInputDto":{"type":"object","properties":{"name":{"type":"string","example":"capacity"},"required":{"type":"boolean","example":true}},"required":["name","required"]},"CreateVerticalProfileDto":{"type":"object","properties":{"key":{"type":"string","example":"nail_bar"},"family":{"type":"string","enum":["RENTALS","BEAUTY"]},"shape":{"type":"string","enum":["UNIT_PER_DAY","SERVICE_IN_SLOT"]},"labels":{"type":"object","additionalProperties":{"type":"string"},"example":{"unit":"service","units":"services"}},"fields":{"type":"array","items":{"$ref":"#/components/schemas/VerticalFieldInputDto"}},"defaultPolicies":{"$ref":"#/components/schemas/PoliciesDto"},"filters":{"type":"array","items":{"type":"string","enum":["capacity","price","duration","rating","distance"]}},"toneNotes":{"type":"string","nullable":true},"enabled":{"type":"boolean"}},"required":["key","family","shape","labels","defaultPolicies"]},"PatchVerticalProfileDto":{"type":"object","properties":{"labels":{"type":"object","description":"string → string, ≤ 40 keys","additionalProperties":{"type":"string"},"example":{"unit":"castle","units":"castles"}},"fields":{"type":"array","items":{"$ref":"#/components/schemas/VerticalFieldInputDto"}},"defaultPolicies":{"$ref":"#/components/schemas/PoliciesDto"},"filters":{"type":"array","items":{"type":"string","enum":["capacity","price","duration","rating","distance"]}},"toneNotes":{"type":"string","nullable":true,"description":"the assistant's tone notes for this vertical"},"enabled":{"type":"boolean"}}},"DayAvailabilityDto":{"type":"object","properties":{"date":{"type":"string","example":"2026-09-14"},"status":{"type":"string","enum":["AVAILABLE","AM","PM","BOOKED","HELD","BLOCKED","CLOSED"]},"bookingId":{"type":"string","example":"ckbk9"}},"required":["date","status"]},"UnitAvailabilityDto":{"type":"object","properties":{"unitId":{"type":"string","example":"ckunit1"},"name":{"type":"string","example":"Jungle Castle"},"days":{"type":"array","items":{"$ref":"#/components/schemas/DayAvailabilityDto"}}},"required":["unitId","name","days"]},"AvailabilityDataDto":{"type":"object","properties":{"shape":{"type":"string","enum":["UNIT_PER_DAY"]},"halfDayEnabled":{"type":"boolean","example":false},"units":{"type":"array","items":{"$ref":"#/components/schemas/UnitAvailabilityDto"}}},"required":["shape","halfDayEnabled","units"]},"AvailabilityResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AvailabilityDataDto"}},"required":["data"]},"SlotDto":{"type":"object","properties":{"start":{"type":"string","example":"09:00"},"end":{"type":"string","example":"10:00","description":"Customer-visible end; the buffer is not shown"},"startsAt":{"type":"string","example":"2026-09-15T09:00:00.000Z"},"endsAt":{"type":"string","example":"2026-09-15T10:00:00.000Z"}},"required":["start","end","startsAt","endsAt"]},"ProviderSlotsDto":{"type":"object","properties":{"providerId":{"type":"string","example":"ckprov1"},"displayName":{"type":"string","example":"Ama"},"slots":{"type":"array","items":{"$ref":"#/components/schemas/SlotDto"}}},"required":["providerId","displayName","slots"]},"SlotsDataDto":{"type":"object","properties":{"shape":{"type":"string","enum":["SERVICE_IN_SLOT"]},"date":{"type":"string","example":"2026-09-15"},"stepMin":{"type":"number","example":15},"providers":{"type":"array","items":{"$ref":"#/components/schemas/ProviderSlotsDto"}}},"required":["shape","date","stepMin","providers"]},"SlotsResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SlotsDataDto"}},"required":["data"]},"QuotePointDto":{"type":"object","properties":{"lat":{"type":"number","example":5.6698},"lng":{"type":"number","example":-0.0166}},"required":["lat","lng"]},"QuoteRequestDto":{"type":"object","properties":{"unitId":{"type":"string","example":"ckunit1","description":"UNIT_PER_DAY: the unit being priced"},"date":{"type":"string","example":"2026-09-14","description":"UNIT_PER_DAY: the day"},"halfDay":{"type":"string","enum":["AM","PM"],"nullable":true},"serviceId":{"type":"string","example":"cksvc1","description":"SERVICE_IN_SLOT: the service being priced"},"providerId":{"type":"string","example":"ckprov1"},"slotStart":{"type":"string","example":"2026-09-15T09:00:00.000Z"},"areaId":{"type":"string","example":"ckarea9","description":"Delivery area for AT_CUSTOMER rentals"},"point":{"description":"Delivery point; the nearest area within 8 km is used","allOf":[{"$ref":"#/components/schemas/QuotePointDto"}]},"extraIds":{"default":[],"type":"array","items":{"type":"string"}}}},"QuoteLineDto":{"type":"object","properties":{"kind":{"type":"string","enum":["UNIT","SERVICE","DELIVERY","EXTRA","HALF_DAY_ADJUSTMENT"]},"refId":{"type":"string","nullable":true},"label":{"type":"string","example":"Jungle Castle · Sat 14 Sept"},"amountPesewas":{"type":"number","example":55000}},"required":["kind","refId","label","amountPesewas"]},"QuoteDataDto":{"type":"object","properties":{"covered":{"type":"boolean","example":true,"description":"false when delivery is needed but the area is not covered"},"currency":{"type":"string","example":"GHS"},"lines":{"type":"array","items":{"$ref":"#/components/schemas/QuoteLineDto"}},"subtotalPesewas":{"type":"number","example":78000},"totalPesewas":{"type":"number","example":78000},"depositDuePesewas":{"type":"number","example":20000},"holdTtlMin":{"type":"number","example":120},"policySummary":{"type":"string","example":"Free reschedule up to 48h before. Deposit forfeited if cancelled under 24h."}},"required":["covered","currency","lines","subtotalPesewas","totalPesewas","depositDuePesewas","holdTtlMin","policySummary"]},"QuoteResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/QuoteDataDto"}},"required":["data"]},"HoldContextDto":{"type":"object","properties":{"conversationRef":{"type":"string","example":"C-8F3K","description":"Assistant conversation ref, kept on the held event"}}},"PublicHoldRequestDto":{"type":"object","properties":{"unitId":{"type":"string","example":"ckunit1","description":"UNIT_PER_DAY: the unit being priced"},"date":{"type":"string","example":"2026-09-14","description":"UNIT_PER_DAY: the day"},"halfDay":{"type":"string","enum":["AM","PM"],"nullable":true},"serviceId":{"type":"string","example":"cksvc1","description":"SERVICE_IN_SLOT: the service being priced"},"providerId":{"type":"string","example":"ckprov1"},"slotStart":{"type":"string","example":"2026-09-15T09:00:00.000Z"},"areaId":{"type":"string","example":"ckarea9","description":"Delivery area for AT_CUSTOMER rentals"},"point":{"description":"Delivery point; the nearest area within 8 km is used","allOf":[{"$ref":"#/components/schemas/QuotePointDto"}]},"extraIds":{"default":[],"type":"array","items":{"type":"string"}},"deliveryLine":{"type":"string","example":"House 12, near the school","description":"Delivery address line (rentals)"},"notes":{"type":"string","example":"Party for 15 kids"},"context":{"$ref":"#/components/schemas/HoldContextDto"}}},"HoldPaymentDto":{"type":"object","properties":{"depositDuePesewas":{"type":"number","example":20000},"instructions":{"type":"string","nullable":true,"description":"How the business takes deposits (F27a); null until the owner writes it"},"holdExpiresAt":{"type":"string","example":"2026-09-05T12:41:00.000Z","nullable":true}},"required":["depositDuePesewas","instructions","holdExpiresAt"]},"HoldDataDto":{"type":"object","properties":{"booking":{"type":"object","description":"docs/API.md \"Booking object\" (status HELD)"},"payment":{"$ref":"#/components/schemas/HoldPaymentDto"}},"required":["booking","payment"]},"HoldResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/HoldDataDto"}},"required":["data"]},"CreateRefDto":{"type":"object","properties":{"unitId":{"type":"string","example":"ckunit1"},"serviceId":{"type":"string","example":"cksvc1"},"providerId":{"type":"string","example":"ckprov1"},"date":{"type":"string","example":"2026-09-14"},"slotStart":{"type":"string","example":"2026-09-15T11:00:00.000Z"},"areaId":{"type":"string","example":"ckarea1"}}},"RefResponseDto":{"type":"object","properties":{"ref":{"type":"string","example":"C-8F3K"},"waLink":{"type":"string","example":"https://wa.me/233551234567?text=Hi%2C%20I%27m%20looking%20at%20…%20(ref%20C-8F3K)"},"expiresAt":{"type":"string"}},"required":["ref","waLink","expiresAt"]},"HoldCustomerDto":{"type":"object","properties":{"phone":{"type":"string","example":"+233241234567","description":"E.164; the customer row is upserted per business"},"name":{"type":"string","example":"Ama Mensah"}},"required":["phone"]},"HoldRequestDto":{"type":"object","properties":{"unitId":{"type":"string","example":"ckunit1","description":"UNIT_PER_DAY: the unit being priced"},"date":{"type":"string","example":"2026-09-14","description":"UNIT_PER_DAY: the day"},"halfDay":{"type":"string","enum":["AM","PM"],"nullable":true},"serviceId":{"type":"string","example":"cksvc1","description":"SERVICE_IN_SLOT: the service being priced"},"providerId":{"type":"string","example":"ckprov1"},"slotStart":{"type":"string","example":"2026-09-15T09:00:00.000Z"},"areaId":{"type":"string","example":"ckarea9","description":"Delivery area for AT_CUSTOMER rentals"},"point":{"description":"Delivery point; the nearest area within 8 km is used","allOf":[{"$ref":"#/components/schemas/QuotePointDto"}]},"extraIds":{"default":[],"type":"array","items":{"type":"string"}},"customer":{"description":"Who is booking; optional for owner-made holds","allOf":[{"$ref":"#/components/schemas/HoldCustomerDto"}]},"source":{"type":"string","enum":["WHATSAPP","APP","MANUAL","API"],"description":"Where the booking came from; defaults to API for keys and MANUAL for owner/staff sessions"},"deliveryLine":{"type":"string","example":"House 12, near the school","description":"Delivery address line (rentals)"},"notes":{"type":"string","example":"Party for 15 kids"},"context":{"$ref":"#/components/schemas/HoldContextDto"}}},"CheckoutDto":{"type":"object","properties":{"plan":{"type":"string","enum":["STARTER","PLUS"]}},"required":["plan"]},"CheckoutResponseDto":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]},"CustomerDto":{"type":"object","properties":{"id":{"type":"string","example":"ckcust1"},"phone":{"type":"string","example":"+233241234567"},"name":{"type":"string","nullable":true},"noShowCount":{"type":"number","example":0},"source":{"type":"string","enum":["WHATSAPP","APP","MANUAL","API"]},"createdAt":{"type":"string","example":"2026-09-06T10:00:00.000Z"}},"required":["id","phone","name","noShowCount","source","createdAt"]},"CustomerPageMetaDto":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"},"limit":{"type":"number","example":20}},"required":["nextCursor","hasMore","limit"]},"CustomerListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CustomerDto"}},"meta":{"$ref":"#/components/schemas/CustomerPageMetaDto"}},"required":["data","meta"]},"CustomerResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CustomerDto"}},"required":["data"]},"DigestResponseDto":{"type":"object","properties":{"data":{"type":"object","description":"docs/API.md digest: date, bookings[], counts, totals"}},"required":["data"]},"UnitDto":{"type":"object","properties":{"id":{"type":"string","example":"ckunit1"},"name":{"type":"string","example":"Jungle Castle"},"description":{"type":"string","nullable":true},"capacity":{"type":"number","example":18},"sizeLabel":{"type":"string","nullable":true},"pricePerDayPesewas":{"type":"number","example":55000},"halfDayPricePesewas":{"type":"number","nullable":true},"photos":{"type":"array","items":{"type":"string"}},"setupNotes":{"type":"string","nullable":true},"active":{"type":"boolean","example":true},"createdAt":{"type":"string","example":"2026-09-06T10:00:00.000Z"}},"required":["id","name","description","capacity","sizeLabel","pricePerDayPesewas","halfDayPricePesewas","photos","setupNotes","active","createdAt"]},"UnitListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/UnitDto"}}},"required":["data"]},"CreateUnitDto":{"type":"object","properties":{"name":{"type":"string","example":"Jungle Castle"},"description":{"type":"string","example":"4m × 4m, slide"},"capacity":{"type":"number","example":18,"description":"People the unit takes, 1..500"},"sizeLabel":{"type":"string","example":"4m × 4m"},"pricePerDayPesewas":{"type":"number","example":55000,"description":"Integer pesewas"},"halfDayPricePesewas":{"type":"object","example":null,"nullable":true,"description":"Integer pesewas; null disables half days for this unit"},"photos":{"example":["https://minio/chale-uploads/jungle-1.jpg"],"description":"≤ 10 URLs from POST /uploads","type":"array","items":{"type":"string"}},"setupNotes":{"type":"string","example":"Needs 5m × 5m flat ground, power point within 20m"},"active":{"type":"boolean","default":true}},"required":["name","capacity","pricePerDayPesewas"]},"UnitResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/UnitDto"}},"required":["data"]},"UpdateUnitDto":{"type":"object","properties":{"name":{"type":"string","example":"Jungle Castle"},"description":{"type":"string","example":"4m × 4m, slide"},"capacity":{"type":"number","example":18,"description":"People the unit takes, 1..500"},"sizeLabel":{"type":"string","example":"4m × 4m"},"pricePerDayPesewas":{"type":"number","example":55000,"description":"Integer pesewas"},"halfDayPricePesewas":{"type":"object","example":null,"nullable":true,"description":"Integer pesewas; null disables half days for this unit"},"photos":{"example":["https://minio/chale-uploads/jungle-1.jpg"],"description":"≤ 10 URLs from POST /uploads","type":"array","items":{"type":"string"}},"setupNotes":{"type":"string","example":"Needs 5m × 5m flat ground, power point within 20m"},"active":{"type":"boolean","default":true}}},"ServiceDto":{"type":"object","properties":{"id":{"type":"string","example":"cksvc1"},"name":{"type":"string","example":"Knotless braids (medium)"},"durationMin":{"type":"number","example":180},"bufferMin":{"type":"number","example":15},"pricePesewas":{"type":"number","example":25000},"category":{"type":"string","nullable":true},"active":{"type":"boolean","example":true},"createdAt":{"type":"string","example":"2026-09-06T10:00:00.000Z"}},"required":["id","name","durationMin","bufferMin","pricePesewas","category","active","createdAt"]},"ServiceListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ServiceDto"}}},"required":["data"]},"CreateServiceDto":{"type":"object","properties":{"name":{"type":"string","example":"Knotless braids (medium)"},"durationMin":{"type":"number","example":180,"description":"5..480 minutes, in 5-minute steps"},"bufferMin":{"type":"number","example":15,"description":"Clean-up time after the service, 0..120"},"pricePesewas":{"type":"number","example":25000,"description":"Integer pesewas"},"category":{"type":"string","example":"braids"},"active":{"type":"boolean","default":true}},"required":["name","durationMin","pricePesewas"]},"ServiceResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ServiceDto"}},"required":["data"]},"UpdateServiceDto":{"type":"object","properties":{"name":{"type":"string","example":"Knotless braids (medium)"},"durationMin":{"type":"number","example":180,"description":"5..480 minutes, in 5-minute steps"},"bufferMin":{"type":"number","example":15,"description":"Clean-up time after the service, 0..120"},"pricePesewas":{"type":"number","example":25000,"description":"Integer pesewas"},"category":{"type":"string","example":"braids"},"active":{"type":"boolean","default":true}}},"ScheduleBreakDto":{"type":"object","properties":{"start":{"type":"string","example":"13:00"},"end":{"type":"string","example":"14:00"}},"required":["start","end"]},"ScheduleDayDto":{"type":"object","properties":{"weekday":{"type":"number","example":2,"description":"0 = Sunday … 6 = Saturday"},"start":{"type":"string","example":"09:00"},"end":{"type":"string","example":"18:00"},"breaks":{"type":"array","items":{"$ref":"#/components/schemas/ScheduleBreakDto"}}},"required":["weekday","start","end"]},"ProviderDto":{"type":"object","properties":{"id":{"type":"string","example":"ckprov1"},"displayName":{"type":"string","example":"Ama"},"userId":{"type":"string","nullable":true},"active":{"type":"boolean","example":true},"serviceIds":{"description":"Service ids this provider performs","type":"array","items":{"type":"string"}},"schedule":{"type":"array","items":{"$ref":"#/components/schemas/ScheduleDayDto"}},"createdAt":{"type":"string","example":"2026-09-06T10:00:00.000Z"}},"required":["id","displayName","userId","active","serviceIds","schedule","createdAt"]},"ProviderListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ProviderDto"}}},"required":["data"]},"ProviderResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ProviderDto"}},"required":["data"]},"CreateProviderDto":{"type":"object","properties":{"displayName":{"type":"string","example":"Ama"},"userId":{"type":"object","example":null,"nullable":true,"description":"Chale user id of the staff member, when they have a login"},"active":{"type":"boolean","default":true}},"required":["displayName"]},"UpdateProviderDto":{"type":"object","properties":{"displayName":{"type":"string","example":"Ama"},"userId":{"type":"object","example":null,"nullable":true,"description":"Chale user id of the staff member, when they have a login"},"active":{"type":"boolean","default":true}}},"SetProviderServicesDto":{"type":"object","properties":{"serviceIds":{"description":"The full set of services this provider performs","type":"array","items":{"type":"string"}}},"required":["serviceIds"]},"PutProviderScheduleDto":{"type":"object","properties":{"days":{"description":"The full weekly schedule; days not listed are days off","type":"array","items":{"$ref":"#/components/schemas/ScheduleDayDto"}}},"required":["days"]},"ExtraDto":{"type":"object","properties":{"id":{"type":"string","example":"ckext1"},"name":{"type":"string","example":"Generator"},"pricePesewas":{"type":"number","example":15000},"appliesTo":{"type":"string","enum":["UNIT","SERVICE","ALL"]},"active":{"type":"boolean","example":true},"createdAt":{"type":"string","example":"2026-09-06T10:00:00.000Z"}},"required":["id","name","pricePesewas","appliesTo","active","createdAt"]},"ExtraListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ExtraDto"}}},"required":["data"]},"CreateExtraDto":{"type":"object","properties":{"name":{"type":"string","example":"Generator"},"pricePesewas":{"type":"number","example":15000,"description":"Integer pesewas"},"appliesTo":{"type":"string","enum":["UNIT","SERVICE","ALL"],"default":"ALL"},"active":{"type":"boolean","default":true}},"required":["name","pricePesewas"]},"ExtraResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ExtraDto"}},"required":["data"]},"UpdateExtraDto":{"type":"object","properties":{"name":{"type":"string","example":"Generator"},"pricePesewas":{"type":"number","example":15000,"description":"Integer pesewas"},"appliesTo":{"type":"string","enum":["UNIT","SERVICE","ALL"],"default":"ALL"},"active":{"type":"boolean","default":true}}},"BlockDto":{"type":"object","properties":{"id":{"type":"string","example":"ckblk1"},"unitId":{"type":"string","nullable":true},"providerId":{"type":"string","nullable":true},"startsAt":{"type":"string","example":"2026-09-20T00:00:00.000Z"},"endsAt":{"type":"string","example":"2026-09-21T00:00:00.000Z"},"reason":{"type":"string","nullable":true},"createdAt":{"type":"string","example":"2026-09-06T10:00:00.000Z"}},"required":["id","unitId","providerId","startsAt","endsAt","reason","createdAt"]},"BlockListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/BlockDto"}}},"required":["data"]},"CreateBlockDto":{"type":"object","properties":{"unitId":{"type":"object","example":"ckunit1","nullable":true,"description":"Block one unit; null with providerId null blocks the whole business"},"providerId":{"type":"object","example":null,"nullable":true,"description":"Block one provider"},"startsAt":{"type":"string","example":"2026-09-20T00:00:00.000Z"},"endsAt":{"type":"string","example":"2026-09-21T00:00:00.000Z"},"reason":{"type":"string","example":"Maintenance"}},"required":["startsAt","endsAt"]},"BlockResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/BlockDto"}},"required":["data"]},"BookingPageMetaDto":{"type":"object","properties":{"nextCursor":{"type":"string","nullable":true},"hasMore":{"type":"boolean"},"limit":{"type":"number","example":20}},"required":["nextCursor","hasMore","limit"]},"BookingListResponseDto":{"type":"object","properties":{"data":{"description":"Booking objects ordered by startsAt","type":"array","items":{"type":"object"}},"meta":{"$ref":"#/components/schemas/BookingPageMetaDto"}},"required":["data","meta"]},"ManualBookingDto":{"type":"object","properties":{"unitId":{"type":"string","example":"ckunit1","description":"UNIT_PER_DAY: the unit being priced"},"date":{"type":"string","example":"2026-09-14","description":"UNIT_PER_DAY: the day"},"halfDay":{"type":"string","enum":["AM","PM"],"nullable":true},"serviceId":{"type":"string","example":"cksvc1","description":"SERVICE_IN_SLOT: the service being priced"},"providerId":{"type":"string","example":"ckprov1"},"slotStart":{"type":"string","example":"2026-09-15T09:00:00.000Z"},"areaId":{"type":"string","example":"ckarea9","description":"Delivery area for AT_CUSTOMER rentals"},"point":{"description":"Delivery point; the nearest area within 8 km is used","allOf":[{"$ref":"#/components/schemas/QuotePointDto"}]},"extraIds":{"default":[],"type":"array","items":{"type":"string"}},"customer":{"description":"Who is booking; optional for owner-made holds","allOf":[{"$ref":"#/components/schemas/HoldCustomerDto"}]},"source":{"type":"string","enum":["WHATSAPP","APP","MANUAL","API"],"description":"Where the booking came from; defaults to API for keys and MANUAL for owner/staff sessions"},"deliveryLine":{"type":"string","example":"House 12, near the school","description":"Delivery address line (rentals)"},"notes":{"type":"string","example":"Party for 15 kids"},"context":{"$ref":"#/components/schemas/HoldContextDto"},"confirm":{"type":"boolean","default":true,"description":"true (default): CONFIRMED at once — deposit collected offline or waived; false: HELD with the TTL"}}},"BookingResponseDto":{"type":"object","properties":{"data":{"type":"object","description":"docs/API.md \"Booking object\""}},"required":["data"]},"ActionNoteDto":{"type":"object","properties":{"note":{"type":"string","example":"Crew left at 09:10","maxLength":300}}},"RescheduleDto":{"type":"object","properties":{"date":{"type":"string","example":"2026-09-21","description":"UNIT_PER_DAY: the new day"},"halfDay":{"type":"string","enum":["AM","PM"],"nullable":true,"description":"UNIT_PER_DAY: keep (omit), set, or clear (null) the half"},"slotStart":{"type":"string","example":"2026-09-15T11:00:00.000Z","description":"SERVICE_IN_SLOT: the new slot start"},"providerId":{"type":"string","example":"ckprov1","description":"SERVICE_IN_SLOT: move to another provider"}}},"RescheduleDataDto":{"type":"object","properties":{"booking":{"type":"object"},"free":{"type":"boolean","example":true,"description":"within freeRescheduleHours"},"feePesewas":{"type":"number","example":0},"hoursBeforeStart":{"type":"number","example":52.5}},"required":["booking","free","feePesewas","hoursBeforeStart"]},"RescheduleResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/RescheduleDataDto"}},"required":["data"]},"CancelBookingDto":{"type":"object","properties":{"by":{"type":"string","enum":["CUSTOMER","BUSINESS"],"description":"Who is cancelling; drives the policy outcome"},"reason":{"type":"string","example":"Rain forecast","maxLength":300}},"required":["by"]},"CancelOutcomeDto":{"type":"object","properties":{"refundablePesewas":{"type":"number","example":20000},"forfeitedPesewas":{"type":"number","example":0},"rule":{"type":"string","enum":["FULL_REFUND","DEPOSIT_FORFEITED","BUSINESS_CANCELLED_FULL_REFUND","NOTHING_PAID"]},"hoursBeforeStart":{"type":"number","example":52.5}},"required":["refundablePesewas","forfeitedPesewas","rule","hoursBeforeStart"]},"CancelResponseDataDto":{"type":"object","properties":{"booking":{"type":"object"},"outcome":{"$ref":"#/components/schemas/CancelOutcomeDto"}},"required":["booking","outcome"]},"CancelResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CancelResponseDataDto"}},"required":["data"]},"RecordPaymentDto":{"type":"object","properties":{"kind":{"type":"string","enum":["DEPOSIT","BALANCE"]},"amountPesewas":{"type":"number","example":20000,"description":"Pesewas; > 0 and ≤ the outstanding balance"},"method":{"type":"string","enum":["CASH","MOMO_DIRECT"]},"reference":{"type":"string","example":"MP240911.1234.A56789","description":"MoMo transaction id or receipt number"},"note":{"type":"string","example":"Paid at delivery"}},"required":["kind","amountPesewas","method"]},"PaymentResponseDataDto":{"type":"object","properties":{"payment":{"type":"object"},"booking":{"type":"object","description":"The booking after the change (status may have become CONFIRMED)"}},"required":["payment","booking"]},"PaymentResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PaymentResponseDataDto"}},"required":["data"]},"PaymentLinkDto":{"type":"object","properties":{"authorizationUrl":{"type":"string","description":"where the customer pays (Paystack checkout, or the simulator page on staging)"},"reference":{"type":"string"},"amountPesewas":{"type":"number"},"expiresAt":{"type":"string"},"paymentId":{"type":"string"}},"required":["authorizationUrl","reference","amountPesewas","expiresAt","paymentId"]},"PaymentLinkResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PaymentLinkDto"}},"required":["data"]},"PaymentListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object"}}},"required":["data"]},"RefundPaymentDto":{"type":"object","properties":{"amountPesewas":{"type":"number","example":20000,"description":"Pesewas; ≤ what is left of the payment"},"note":{"type":"string","example":"Business cancelled, sent back by MoMo"}},"required":["amountPesewas"]},"PayoutDto":{"type":"object","properties":{"status":{"type":"string","enum":["NOT_SET","ACTIVE"]},"bank":{"type":"string","nullable":true},"accountMasked":{"type":"string","nullable":true,"description":"last 4 digits only"}},"required":["status","bank","accountMasked"]},"FinanceSummaryDto":{"type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"},"depositsPesewas":{"type":"number","description":"deposits received in the range (PAID rows, kind DEPOSIT)"},"collectedPesewas":{"type":"number","description":"all money received in the range"},"byMethod":{"type":"object","description":"collected by method: CASH, MOMO_DIRECT, PAYSTACK"},"balancesDuePesewas":{"type":"number","description":"total − paid on live bookings (HELD/CONFIRMED/CHECKED_IN), any date"},"refundedPesewas":{"type":"number"},"payouts":{"$ref":"#/components/schemas/PayoutDto"},"subscription":{"type":"object"}},"required":["from","to","depositsPesewas","collectedPesewas","byMethod","balancesDuePesewas","refundedPesewas","payouts","subscription"]},"PayoutOnboardDto":{"type":"object","properties":{"settlementBank":{"type":"string","description":"Paystack settlement bank code, or a MoMo provider code (MTN, VOD, ATL)","example":"MTN"},"accountNumber":{"type":"string","example":"0241234567"},"accountName":{"type":"string","example":"Ama Mensah"}},"required":["settlementBank","accountNumber","accountName"]},"CreateWebhookDto":{"type":"object","properties":{"url":{"type":"string","example":"https://example.com/chale"},"events":{"type":"array","example":["booking.confirmed"],"items":{"type":"string","enum":["booking.held","booking.confirmed","booking.checked_in","booking.completed","booking.cancelled","booking.no_show","booking.expired","booking.rescheduled","payment.received","payment.refunded","handoff.requested","review.created","member.added","member.removed","subscription.updated","ping"]}},"description":{"type":"string","example":"POS sync"}},"required":["url","events"]},"WebhookResponseDto":{"type":"object","properties":{"data":{"type":"object"}},"required":["data"]},"WebhookListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object"}}},"required":["data"]},"UpdateWebhookDto":{"type":"object","properties":{"url":{"type":"string","example":"https://example.com/chale"},"events":{"type":"array","items":{"type":"string","enum":["booking.held","booking.confirmed","booking.checked_in","booking.completed","booking.cancelled","booking.no_show","booking.expired","booking.rescheduled","payment.received","payment.refunded","handoff.requested","review.created","member.added","member.removed","subscription.updated","ping"]}},"description":{"type":"object","nullable":true},"enabled":{"type":"boolean"}}},"DeliveryListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object"}},"meta":{"type":"object"}},"required":["data","meta"]},"UsageReportDto":{"type":"object","properties":{"day":{"type":"string","example":"2026-09-05"},"metrics":{"type":"object","example":{"meta.messages":42,"llm.cost_pesewas":310},"description":"Metric → quantity to add (integer, 0..1000000). Known metrics: meta.messages.sent, meta.messages.received, meta.templates, llm.turns, llm.input_tokens, llm.output_tokens, llm.cost_pesewas, stt.seconds, voice_notes, handoffs"}},"required":["day","metrics"]},"UsageDayDto":{"type":"object","properties":{"day":{"type":"string","example":"2026-09-05"},"metrics":{"type":"object","example":{"meta.messages":42}}},"required":["day","metrics"]},"UsageResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/UsageDayDto"}},"required":["data"]},"DeviceListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DeviceDto"}}},"required":["data"]},"RegisterDeviceDto":{"type":"object","properties":{"platform":{"type":"string","enum":["android","ios"]},"pushToken":{"type":"string","description":"FCM registration token; under PUSH_PROVIDER=log (F76a) the stable token the app minted for itself"},"appVersion":{"type":"string","example":"1.0.0"}},"required":["platform","pushToken"]},"DeviceResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DeviceDto"}},"required":["data"]},"NotificationDto":{"type":"object","properties":{"id":{"type":"string"},"businessId":{"type":"string"},"category":{"type":"string","example":"booking"},"title":{"type":"string"},"body":{"type":"string"},"data":{"type":"object","description":"deep-link data: kind, bookingId | handoffRef…"},"status":{"type":"string","enum":["SENT","LOGGED","FAILED"]},"createdAt":{"type":"string"}},"required":["id","businessId","category","title","body","data","status","createdAt"]},"NotificationListResponseDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/NotificationDto"}},"meta":{"type":"object"}},"required":["data","meta"]},"CreateUploadDto":{"type":"object","properties":{"kind":{"type":"string","enum":["unit_photo","business_photo","verification_document"]},"contentType":{"type":"string","enum":["image/jpeg","image/png","image/webp","application/pdf"]},"size":{"type":"number","description":"bytes, ≤ 5242880"}},"required":["kind","contentType","size"]},"UploadDto":{"type":"object","properties":{"uploadUrl":{"type":"string","description":"PUT the bytes here with the same Content-Type (and Content-Length = size)"},"publicUrl":{"type":"object","description":"where the object is served from once uploaded — null for a private kind (verification documents)","nullable":true},"key":{"type":"string","description":"the object key (what the owner submits for a verification document)"},"expiresAt":{"type":"string"}},"required":["uploadUrl","publicUrl","key","expiresAt"]},"UploadResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/UploadDto"}},"required":["data"]},"SubmitVerificationDto":{"type":"object","properties":{"idDocument":{"type":"string","example":"private/verification_document/ckbiz1/0123456789abcdef01234567.jpg"},"registrationDocument":{"type":"string"},"premisesPhotos":{"description":"1–6 premises photos","type":"array","items":{"type":"string"}},"notes":{"type":"string","maxLength":600}},"required":["idDocument","premisesPhotos"]},"VerificationDecisionDto":{"type":"object","properties":{"note":{"type":"string","description":"Approve: an optional note for the audit row"}}},"VerificationRejectDto":{"type":"object","properties":{"reason":{"type":"string","minLength":10,"example":"The ID photo is unreadable — please retake it in daylight."}},"required":["reason"]},"OpenDisputeDto":{"type":"object","properties":{"reason":{"type":"string","maxLength":600,"example":"The castle arrived three hours late and was torn."}},"required":["reason"]},"AdminOpenDisputeDto":{"type":"object","properties":{"reason":{"type":"string","maxLength":600,"example":"The castle arrived three hours late and was torn."},"bookingId":{"type":"string"}},"required":["reason","bookingId"]},"ResolveDisputeDto":{"type":"object","properties":{"resolution":{"type":"string","enum":["REFUND_FULL","REFUND_PARTIAL","NO_REFUND"]},"amountPesewas":{"type":"number","description":"REFUND_PARTIAL: pesewas to send back (≤ what is left on the booking)"},"note":{"type":"string","minLength":5,"example":"Late delivery confirmed by both sides; half the deposit back."}},"required":["resolution","note"]},"AdminRefundDto":{"type":"object","properties":{"amountPesewas":{"type":"number","example":20000,"description":"Pesewas; ≤ what is left of the payment"},"note":{"type":"string","minLength":5,"example":"Castle arrived 3 hours late — goodwill refund"}},"required":["amountPesewas","note"]},"UpsertFeatureFlagDto":{"type":"object","properties":{"description":{"type":"string","maxLength":200},"enabled":{"type":"boolean","description":"On for every business"},"businessIds":{"description":"Allow-listed businesses when not on globally","type":"array","items":{"type":"string"}}}},"HandoffEventDto":{"type":"object","properties":{"ref":{"type":"string","example":"H-3K9"},"question":{"type":"string","example":"Setup on sand at Sakumono beach?"},"customerPhone":{"type":"string","example":"+233241234567"},"conversationId":{"type":"string","example":"ckconv1","description":"The assistant's conversation id (opaque to the api)"}},"required":["ref","question","customerPhone","conversationId"]},"CreateReviewDto":{"type":"object","properties":{"rating":{"type":"number","example":5,"description":"1..5"},"text":{"type":"string","example":"Ama did my braids in 3 hours flat. Neat and gentle."}},"required":["rating"]},"ReviewDto":{"type":"object","properties":{"id":{"type":"string"},"bookingId":{"type":"string"},"businessId":{"type":"string"},"rating":{"type":"number","example":5},"text":{"type":"string","nullable":true},"reply":{"type":"string","nullable":true},"repliedAt":{"type":"string","nullable":true},"status":{"type":"string","enum":["PUBLISHED","HIDDEN"]},"createdAt":{"type":"string"}},"required":["id","bookingId","businessId","rating","text","reply","repliedAt","status","createdAt"]},"ReviewResponseDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ReviewDto"}},"required":["data"]},"ReplyReviewDto":{"type":"object","properties":{"text":{"type":"string","example":"Thank you, Ama! See you next time."}},"required":["text"]},"HideReviewDto":{"type":"object","properties":{"reason":{"type":"string","minLength":10,"example":"Contains a phone number and insults the stylist."}},"required":["reason"]},"CreateCalendarFeedDto":{"type":"object","properties":{"providerId":{"type":"string","description":"one provider's bookings only (SERVICE_IN_SLOT businesses)"}}},"CalendarFeedDto":{"type":"object","properties":{"id":{"type":"string"},"providerId":{"type":"string","nullable":true},"createdAt":{"type":"string"},"lastFetchedAt":{"type":"string","nullable":true},"url":{"type":"string","description":"shown once on creation"}},"required":["id","providerId","createdAt","lastFetchedAt"]}}}}