{"openapi":"3.0.3","info":{"title":"cluuub Platform API","version":"1.2.0","description":"Whitelabel multi-tenant club & sports organization API. Authenticated endpoints use token bucket rate limiting (60 req bucket, 1 req/s refill, 10,000 req/day UTC). Sensitive member data requires authorized scopes.","contact":{"name":"cluuub Developer Support","url":"https://cluuub.app/developer"}},"servers":[{"url":"https://sv-halle.cluuub.app/api/v1","description":"cluuub API Gateway"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API Key obtained from club admin dashboard under /dashboard/developer"}},"schemas":{"Problem":{"type":"object","required":["type","title","status","detail","instance"],"properties":{"type":{"type":"string"},"title":{"type":"string"},"status":{"type":"integer"},"detail":{"type":"string"},"instance":{"type":"string"}}},"Group":{"type":"object","required":["id","name","sport"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"sport":{"type":"string"},"age_min":{"type":"integer","nullable":true},"age_max":{"type":"integer","nullable":true},"level":{"type":"string","nullable":true},"description":{"type":"string","nullable":true}}},"Event":{"type":"object","required":["id","title","starts_at"],"properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"starts_at":{"type":"string","format":"date-time"},"ends_at":{"type":"string","format":"date-time","nullable":true},"location":{"type":"string","nullable":true}}},"News":{"type":"object","required":["id","title","content","published_at"],"properties":{"id":{"type":"string","format":"uuid"},"title":{"type":"string"},"content":{"type":"string"},"published_at":{"type":"string","format":"date-time"},"created_by":{"type":"string","format":"uuid"}}},"CompetitionResult":{"type":"object","required":["id","member_id","group_id","competition_name"],"properties":{"id":{"type":"string","format":"uuid"},"member_id":{"type":"string","format":"uuid"},"group_id":{"type":"string","format":"uuid"},"competition_name":{"type":"string"},"discipline":{"type":"string","nullable":true},"placement":{"type":"string","nullable":true},"points":{"type":"number","nullable":true},"competition_date":{"type":"string","format":"date","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"CourtBookingSlot":{"type":"object","required":["court_id","start_time","end_time","status"],"properties":{"court_id":{"type":"string","format":"uuid"},"court_name":{"type":"string"},"start_time":{"type":"string","format":"date-time"},"end_time":{"type":"string","format":"date-time"},"status":{"type":"string","enum":["available","reserved","booked","blocked"]}}}}},"paths":{"/groups":{"get":{"summary":"List active training groups","description":"Required scope: read:groups. Returns data exclusively for the authenticated club tenant. Active training groups and sections.","operationId":"listGroups","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Maximum number of records to return."},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"maximum":10000,"default":0},"description":"Number of records to skip for pagination."}],"responses":{"200":{"description":"Successful response","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Token bucket capacity: 60 requests; refill rate: 1 request per second."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Immediately available request tokens after this call."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix timestamp in seconds when the bucket will be completely full without further requests."},"X-RateLimit-Day-Limit":{"schema":{"type":"integer"},"description":"10,000 authorized requests per UTC calendar day."},"X-RateLimit-Day-Remaining":{"schema":{"type":"integer"},"description":"Remaining authorized requests for the current UTC day."},"X-RateLimit-Day-Reset":{"schema":{"type":"integer"},"description":"Next daily quota reset at 00:00 UTC as Unix timestamp in seconds."}},"content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Group"}},"meta":{"type":"object","properties":{"limit":{"type":"integer"},"offset":{"type":"integer"},"total":{"type":"integer"}}}}}}}},"400":{"description":"Invalid query parameters","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Unauthorized or invalid API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Forbidden - missing required scope","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded (token bucket or daily quota exhausted)","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Token bucket capacity: 60 requests; refill rate: 1 request per second."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Immediately available request tokens after this call."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix timestamp in seconds when the bucket will be completely full without further requests."},"X-RateLimit-Day-Limit":{"schema":{"type":"integer"},"description":"10,000 authorized requests per UTC calendar day."},"X-RateLimit-Day-Remaining":{"schema":{"type":"integer"},"description":"Remaining authorized requests for the current UTC day."},"X-RateLimit-Day-Reset":{"schema":{"type":"integer"},"description":"Next daily quota reset at 00:00 UTC as Unix timestamp in seconds."},"Retry-After":{"schema":{"type":"integer"},"description":"Wait time in seconds until the next token is replenished or daily reset occurs."}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Service temporarily unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/events":{"get":{"summary":"List public events and bookings","description":"Required scope: read:events. Returns data exclusively for the authenticated club tenant. Open events not restricted to closed training groups.","operationId":"listEvents","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Maximum number of records to return."},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"maximum":10000,"default":0},"description":"Number of records to skip for pagination."}],"responses":{"200":{"description":"Successful response","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Token bucket capacity: 60 requests; refill rate: 1 request per second."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Immediately available request tokens after this call."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix timestamp in seconds when the bucket will be completely full without further requests."},"X-RateLimit-Day-Limit":{"schema":{"type":"integer"},"description":"10,000 authorized requests per UTC calendar day."},"X-RateLimit-Day-Remaining":{"schema":{"type":"integer"},"description":"Remaining authorized requests for the current UTC day."},"X-RateLimit-Day-Reset":{"schema":{"type":"integer"},"description":"Next daily quota reset at 00:00 UTC as Unix timestamp in seconds."}},"content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Event"}},"meta":{"type":"object","properties":{"limit":{"type":"integer"},"offset":{"type":"integer"},"total":{"type":"integer"}}}}}}}},"400":{"description":"Invalid query parameters","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Unauthorized or invalid API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Forbidden - missing required scope","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded (token bucket or daily quota exhausted)","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Token bucket capacity: 60 requests; refill rate: 1 request per second."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Immediately available request tokens after this call."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix timestamp in seconds when the bucket will be completely full without further requests."},"X-RateLimit-Day-Limit":{"schema":{"type":"integer"},"description":"10,000 authorized requests per UTC calendar day."},"X-RateLimit-Day-Remaining":{"schema":{"type":"integer"},"description":"Remaining authorized requests for the current UTC day."},"X-RateLimit-Day-Reset":{"schema":{"type":"integer"},"description":"Next daily quota reset at 00:00 UTC as Unix timestamp in seconds."},"Retry-After":{"schema":{"type":"integer"},"description":"Wait time in seconds until the next token is replenished or daily reset occurs."}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Service temporarily unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/news":{"get":{"summary":"List published club news and announcements","description":"Required scope: read:news. Returns data exclusively for the authenticated club tenant. Published team and club bulletins.","operationId":"listNewss","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Maximum number of records to return."},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"maximum":10000,"default":0},"description":"Number of records to skip for pagination."}],"responses":{"200":{"description":"Successful response","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Token bucket capacity: 60 requests; refill rate: 1 request per second."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Immediately available request tokens after this call."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix timestamp in seconds when the bucket will be completely full without further requests."},"X-RateLimit-Day-Limit":{"schema":{"type":"integer"},"description":"10,000 authorized requests per UTC calendar day."},"X-RateLimit-Day-Remaining":{"schema":{"type":"integer"},"description":"Remaining authorized requests for the current UTC day."},"X-RateLimit-Day-Reset":{"schema":{"type":"integer"},"description":"Next daily quota reset at 00:00 UTC as Unix timestamp in seconds."}},"content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/News"}},"meta":{"type":"object","properties":{"limit":{"type":"integer"},"offset":{"type":"integer"},"total":{"type":"integer"}}}}}}}},"400":{"description":"Invalid query parameters","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Unauthorized or invalid API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Forbidden - missing required scope","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded (token bucket or daily quota exhausted)","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Token bucket capacity: 60 requests; refill rate: 1 request per second."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Immediately available request tokens after this call."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix timestamp in seconds when the bucket will be completely full without further requests."},"X-RateLimit-Day-Limit":{"schema":{"type":"integer"},"description":"10,000 authorized requests per UTC calendar day."},"X-RateLimit-Day-Remaining":{"schema":{"type":"integer"},"description":"Remaining authorized requests for the current UTC day."},"X-RateLimit-Day-Reset":{"schema":{"type":"integer"},"description":"Next daily quota reset at 00:00 UTC as Unix timestamp in seconds."},"Retry-After":{"schema":{"type":"integer"},"description":"Wait time in seconds until the next token is replenished or daily reset occurs."}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Service temporarily unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/competition-results":{"get":{"summary":"List competition results","description":"Required scope: read:competition_results. Returns data exclusively for the authenticated club tenant. Official club tournament and contest outcomes.","operationId":"listCompetitionResults","parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"description":"Maximum number of records to return."},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"maximum":10000,"default":0},"description":"Number of records to skip for pagination."}],"responses":{"200":{"description":"Successful response","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Token bucket capacity: 60 requests; refill rate: 1 request per second."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Immediately available request tokens after this call."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix timestamp in seconds when the bucket will be completely full without further requests."},"X-RateLimit-Day-Limit":{"schema":{"type":"integer"},"description":"10,000 authorized requests per UTC calendar day."},"X-RateLimit-Day-Remaining":{"schema":{"type":"integer"},"description":"Remaining authorized requests for the current UTC day."},"X-RateLimit-Day-Reset":{"schema":{"type":"integer"},"description":"Next daily quota reset at 00:00 UTC as Unix timestamp in seconds."}},"content":{"application/json":{"schema":{"type":"object","required":["data","meta"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CompetitionResult"}},"meta":{"type":"object","properties":{"limit":{"type":"integer"},"offset":{"type":"integer"},"total":{"type":"integer"}}}}}}}},"400":{"description":"Invalid query parameters","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"Unauthorized or invalid API key","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"Forbidden - missing required scope","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Rate limit exceeded (token bucket or daily quota exhausted)","headers":{"X-RateLimit-Limit":{"schema":{"type":"integer"},"description":"Token bucket capacity: 60 requests; refill rate: 1 request per second."},"X-RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Immediately available request tokens after this call."},"X-RateLimit-Reset":{"schema":{"type":"integer"},"description":"Unix timestamp in seconds when the bucket will be completely full without further requests."},"X-RateLimit-Day-Limit":{"schema":{"type":"integer"},"description":"10,000 authorized requests per UTC calendar day."},"X-RateLimit-Day-Remaining":{"schema":{"type":"integer"},"description":"Remaining authorized requests for the current UTC day."},"X-RateLimit-Day-Reset":{"schema":{"type":"integer"},"description":"Next daily quota reset at 00:00 UTC as Unix timestamp in seconds."},"Retry-After":{"schema":{"type":"integer"},"description":"Wait time in seconds until the next token is replenished or daily reset occurs."}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Service temporarily unavailable","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/public/{clubSlug}/courts":{"get":{"summary":"List public court availability","description":"Public endpoint listing court slots and occupancy for a club.","operationId":"listPublicCourts","security":[],"parameters":[{"name":"clubSlug","in":"path","required":true,"schema":{"type":"string"},"description":"Slug identifier of the club."},{"name":"date","in":"query","schema":{"type":"string","format":"date"},"description":"Date to inspect (YYYY-MM-DD)."}],"responses":{"200":{"description":"Court availability retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"courts":{"type":"array","items":{"$ref":"#/components/schemas/CourtBookingSlot"}}}}}}},"404":{"description":"Club not found","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/public/{clubSlug}/courts/book":{"post":{"summary":"Book court slot (Public/Guest)","description":"Initiates a court booking for a guest or club member.","operationId":"bookPublicCourt","security":[],"parameters":[{"name":"clubSlug","in":"path","required":true,"schema":{"type":"string"},"description":"Slug identifier of the club."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["court_id","start_time","end_time","booker_name","booker_email"],"properties":{"court_id":{"type":"string","format":"uuid"},"start_time":{"type":"string","format":"date-time"},"end_time":{"type":"string","format":"date-time"},"booker_name":{"type":"string"},"booker_email":{"type":"string","format":"email"},"notes":{"type":"string"}}}}}},"responses":{"201":{"description":"Booking confirmed successfully","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"booking_id":{"type":"string","format":"uuid"}}}}}},"400":{"description":"Slot conflict or invalid booking request","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}}}}