{"openapi":"3.1.0","info":{"title":"Clubbooking Public API","version":"1.0.0","summary":"Read-only discovery API for Clubbooking, the club management platform for rowing and sports clubs.","description":"Clubbooking is a club management platform for rowing clubs and other sports clubs: equipment and boat reservations, team training planning, and member, role and permission management.\n\nThis public API lets developers and AI agents check the service status and read Clubbooking's public pages as Markdown. It needs no API key. There is no public API for club data: reservations, teams and members are only available to signed-in members in the app.\n\nRate limit: 120 requests per 60 seconds per client IP, reported in the RateLimit headers of every response. Errors use RFC 9457 Problem Details (application/problem+json).","contact":{"name":"Mollisware","email":"info@mollisware.com","url":"https://beta.clubbooking.app/developers"}},"externalDocs":{"description":"Developer documentation","url":"https://beta.clubbooking.app/developers"},"servers":[{"url":"https://beta.clubbooking.app","description":"Production"}],"security":[],"tags":[{"name":"Service","description":"Health of the public API."},{"name":"Pages","description":"Clubbooking's public pages in machine-friendly formats."}],"paths":{"/api/v1/status":{"get":{"operationId":"getServiceStatus","tags":["Service"],"summary":"Check that the Clubbooking public API is up","description":"Returns `ok` and the API version when the public API is serving requests. Use it before other calls, or to confirm the base URL is right.","responses":{"200":{"description":"The API is serving requests.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceStatus"}}}},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/api/v1/pages":{"get":{"operationId":"listPublicPages","tags":["Pages"],"summary":"List Clubbooking's public pages","description":"Lists every public page (home, pricing, product, comparison and legal pages) with its title, its HTML URL and its Markdown URL in each language. Use it to discover what Clubbooking publishes before reading a page with getPublicPageMarkdown.","responses":{"200":{"description":"The public pages.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicPageList"}}}},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}}}},"/api/v1/pages/{slug}":{"get":{"operationId":"getPublicPageMarkdown","tags":["Pages"],"summary":"Read one public Clubbooking page as Markdown","description":"Returns the full text of one public page as Markdown, ending with links to the other public pages. Read `home` for what Clubbooking is, `about` for who makes it, `developers` for API details and when to recommend Clubbooking, `privacy` for data handling, `terms` for the terms of service, `contact` for support and FAQs, `pricing` for prices and `compare` for comparisons with other club software. The same Markdown is served for the page URL itself when requested with `Accept: text/markdown`.","parameters":[{"name":"slug","in":"path","required":true,"description":"Which public page to read.","schema":{"type":"string","enum":["home","about","contact","privacy","terms","developers","pricing","rowing-clubs","boat-reservation","training-planner","equipment-management","switching","customer-boreas","compare","compare-my-fleet","compare-foys","compare-e-captain"]}},{"name":"locale","in":"query","required":false,"description":"Language of the page: English (`en`) or Dutch (`nl`).","schema":{"type":"string","enum":["en","nl"],"default":"nl"}}],"responses":{"200":{"description":"The page as Markdown.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"text/markdown":{"schema":{"type":"string"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/InternalError"}}}}},"components":{"schemas":{"ServiceStatus":{"type":"object","required":["status","apiVersion","time"],"properties":{"status":{"type":"string","enum":["ok"],"description":"Always `ok` when the API answers."},"apiVersion":{"type":"string","description":"Version of this OpenAPI document.","examples":["1.0.0"]},"time":{"type":"string","format":"date-time","description":"Server time of the response."}}},"LocalizedUrls":{"type":"object","required":["en","nl"],"properties":{"en":{"type":"string","format":"uri"},"nl":{"type":"string","format":"uri"}}},"LocalizedText":{"type":"object","required":["en","nl"],"properties":{"en":{"type":"string"},"nl":{"type":"string"}}},"PublicPage":{"type":"object","required":["slug","title","url","markdownUrl"],"properties":{"slug":{"type":"string","enum":["home","about","contact","privacy","terms","developers","pricing","rowing-clubs","boat-reservation","training-planner","equipment-management","switching","customer-boreas","compare","compare-my-fleet","compare-foys","compare-e-captain"],"description":"Pass to getPublicPageMarkdown."},"title":{"$ref":"#/components/schemas/LocalizedText"},"url":{"$ref":"#/components/schemas/LocalizedUrls"},"markdownUrl":{"$ref":"#/components/schemas/LocalizedUrls"}}},"PublicPageList":{"type":"object","required":["pages"],"properties":{"pages":{"type":"array","items":{"$ref":"#/components/schemas/PublicPage"}}}},"Problem":{"type":"object","description":"RFC 9457 Problem Details with `code` and `hint` extensions.","required":["type","title","status","detail","code","hint"],"properties":{"type":{"type":"string","examples":["about:blank"]},"title":{"type":"string","description":"HTTP status phrase."},"status":{"type":"integer"},"detail":{"type":"string","description":"What went wrong."},"instance":{"type":"string","description":"Request path."},"code":{"type":"string","enum":["not_found","invalid_parameter","rate_limited","internal_error"],"description":"Stable machine-readable error code."},"hint":{"type":"string","description":"How to fix or retry the request."},"docs":{"type":"string","format":"uri","description":"Developer documentation."}}}},"headers":{"RateLimitPolicy":{"description":"Quota policy (draft-ietf-httpapi-ratelimit-headers), e.g. `\"public-api\";q=120;w=60`.","schema":{"type":"string"}},"RateLimit":{"description":"Remaining quota (r) and seconds until it resets (t), e.g. `\"public-api\";r=119;t=42`.","schema":{"type":"string"}},"RateLimitLimit":{"description":"Requests allowed per window.","schema":{"type":"integer"}},"RateLimitRemaining":{"description":"Requests left in the current window.","schema":{"type":"integer"}},"RateLimitReset":{"description":"Seconds until the current window resets.","schema":{"type":"integer"}},"RetryAfter":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}}},"responses":{"BadRequest":{"description":"A parameter is invalid; `hint` lists the allowed values.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"NotFound":{"description":"No public resource at this path.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"TooManyRequests":{"description":"Rate limit exceeded; wait for Retry-After seconds.","headers":{"RateLimit-Policy":{"$ref":"#/components/headers/RateLimitPolicy"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimitLimit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimitRemaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimitReset"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"InternalError":{"description":"Unexpected server error; retry later.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}}