{"openapi":"3.1.0","info":{"title":"Crewboard API","version":"1.0.0","summary":"Outings, ergs, crews and replies for college boat clubs.","description":"Every request acts as the person who created the token (Settings → Agents & calendar).\n\nSend `Authorization: Bearer cb_…`. Read-only tokens may only use GET.\n\nDates are Europe/London days (YYYY-MM-DD); instants are UTC epoch milliseconds.\n\nErrors: `{ error: { code, message } }`. Rate limit: about 120 requests per minute per token (best effort).\n\nRowers never remove themselves from crew seats: reply NO instead. Agents must ask the person before any write.\n\nThe same operations are available as MCP tools at POST /mcp."},"servers":[{"url":"https://trustworthy-moose-24.convex.site/api/v1"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Members","description":"Anything an active member can do for themselves."},{"name":"Captains","description":"Captains, coaches and club admins (checked per squad)."}],"paths":{"/":{"get":{"operationId":"index","summary":"API index","description":"Links to this document, the MCP endpoint and the docs. No token needed.","security":[],"responses":{"200":{"description":"OK"}}}},"/openapi.json":{"get":{"operationId":"openapi","summary":"This OpenAPI document","security":[],"responses":{"200":{"description":"OK"}}}},"/me":{"get":{"operationId":"whoami","summary":"Who am I","description":"Returns the signed-in person (the owner of this API token): name, email and every club membership with its status, display name, admin flag and squads (id, code, name, role). Call this first to learn the club slug and squad ids the other tools need.\n\nMCP tool: `whoami`.","tags":["Members"],"parameters":[],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/clubs/{club}/sessions":{"get":{"operationId":"list_sessions","summary":"List sessions","description":"Lists outings (on the water) and land / erg sessions in a club between two days (inclusive, at most 62 days). Each session has its squad, status (draft | published | cancelled), London date, start/end as UTC epoch ms (null = time TBC), plan, boat, crew counts and `mySeats` (the caller's own seats with seat ids and replies). Drafts only appear for that squad's captains and coaches. Use get_session for the full crew list.\n\nMCP tool: `list_sessions`.","tags":["Members"],"parameters":[{"name":"club","in":"path","required":true,"schema":{"type":"string","description":"Club slug, e.g. \"lccbc\". `whoami` lists the clubs you belong to.","minLength":1,"maxLength":40}},{"name":"from","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","format":"date","description":"First day (default: today) as a Europe/London calendar day, \"YYYY-MM-DD\"."}},{"name":"to","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","format":"date","description":"Last day (default: 13 days after `from`) as a Europe/London calendar day, \"YYYY-MM-DD\"."}},{"name":"squad","in":"query","required":false,"schema":{"type":"string","description":"Only this squad: a squad id or its code such as \"NW\" (Novice Women) or \"NO\" (Novice Open).","minLength":1,"maxLength":64}},{"name":"kind","in":"query","required":false,"schema":{"type":"string","enum":["outing","land"],"description":"Only outings or only land sessions."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/clubs/{club}/me/upcoming":{"get":{"operationId":"my_upcoming_sessions","summary":"My upcoming sessions","description":"The caller's own seats in published or cancelled sessions from today for the next `days` days (default 14, at most 62), soonest first. Each item has the session summary, `seatId`, seat label (Cox, Stroke, 7 … 2, Bow, Sub 1, erg 1 …), seat kind and the current reply (pending | yes | no). This is the best starting point for 'what am I down for this week?'.\n\nMCP tool: `my_upcoming_sessions`.","tags":["Members"],"parameters":[{"name":"club","in":"path","required":true,"schema":{"type":"string","description":"Club slug, e.g. \"lccbc\". `whoami` lists the clubs you belong to.","minLength":1,"maxLength":40}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":62,"description":"How many days ahead (default 14)."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/sessions/{sessionId}":{"get":{"operationId":"get_session","summary":"Get a session","description":"One session in full: everything from list_sessions plus every seat in order (id, kind crew | cox | land | sub | dropout, label, who is in it, their reply and note, `isMe`), notes, revision, cancel reason and what the caller may do (`permissions`).\n\nMCP tool: `get_session`.","tags":["Members"],"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","description":"Session id.","minLength":1,"maxLength":64}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"update_session","summary":"Update a session (captains, coaches)","description":"Changes a session's details. Only the fields you pass change; list field names in `clear` to empty them. Coaches may change title, plan, notes and coach; captains everything. People in a published session are emailed about material changes.\n\nMCP tool: `update_session`. Needs a read-write token.","tags":["Captains"],"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","description":"Session id.","minLength":1,"maxLength":64}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","format":"date","description":"New day as a Europe/London calendar day, \"YYYY-MM-DD\"."},"startTime":{"type":"string","pattern":"^\\d{2}:\\d{2}$","description":"Start (meet) time, 24 h London time \"HH:MM\". Leave out when the time is not known yet (TBC)."},"durationMinutes":{"type":"integer","minimum":5,"maximum":720,"description":"Length in minutes. Default: the club's outing / land length."},"timeNote":{"type":"string","description":"Free-text time when it is not a clean time, e.g. \"after lectures\".","maxLength":80},"title":{"type":"string","description":"Headline of the plan, e.g. \"Steady state + starts\" or \"Ergs: 3x2k\".","maxLength":120},"plan":{"type":"string","description":"The plan for the session (outing plan or land workout).","maxLength":2000},"boatId":{"type":"string","description":"Boat id (outings).","minLength":1,"maxLength":64},"boathouseId":{"type":"string","description":"Boathouse id (outings). Defaults to the boat's boathouse.","minLength":1,"maxLength":64},"location":{"type":"string","description":"Venue for land sessions, e.g. a gym.","maxLength":120},"coach":{"type":"string","description":"Coach name (free text; coaches are often not members).","maxLength":80},"notes":{"type":"string","description":"Notes shown to the crew.","maxLength":2000},"clear":{"type":"array","description":"Fields to empty.","items":{"type":"string","enum":["startTime","timeNote","title","plan","boat","boathouse","location","coach","notes"]}}},"additionalProperties":false}}}}}},"/seats/{seatId}/reply":{"post":{"operationId":"reply_to_seat","summary":"Reply YES or NO","description":"Replies YES (I'll be there) or NO (I can't make it) for one of the caller's own seats in a published session. A NO alerts the captains and asks the squad for a sub, so ALWAYS confirm with the person before replying, and never reply YES on their behalf without asking. Rowers never take their own name out of a crew seat: replying NO is the correct way to drop out. Replies close when the session starts.\n\nMCP tool: `reply_to_seat`. Needs a read-write token.","tags":["Members"],"parameters":[{"name":"seatId","in":"path","required":true,"schema":{"type":"string","description":"Seat id (from my_upcoming_sessions or get_session).","minLength":1,"maxLength":64}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"reply":{"type":"string","enum":["yes","no"]},"note":{"type":"string","description":"Optional short note for the captains, e.g. \"lab until 9\".","maxLength":200}},"required":["reply"],"additionalProperties":false}}}}}},"/sessions/{sessionId}/sign-up":{"post":{"operationId":"sign_up_for_session","summary":"Sign up for a session","description":"Puts the caller into the first free sub slot of a published outing, or the first free erg slot of a land session (then a sub slot), and marks them YES. Fails when they already have a seat, the session has started or nothing is free. Use list_open_slots to find sessions with space. Ask the person before signing them up.\n\nMCP tool: `sign_up_for_session`. Needs a read-write token.","tags":["Members"],"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","description":"Session id.","minLength":1,"maxLength":64}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"as":{"type":"string","enum":["sub","land"],"description":"Optional: force a sub slot or an erg slot (land sessions only)."}},"additionalProperties":false}}}}}},"/seats/{seatId}/withdraw":{"post":{"operationId":"withdraw_from_seat","summary":"Withdraw from a sub or erg slot","description":"Takes the caller out of a sub slot or an erg slot they hold. Not allowed for crew or cox seats: for those, reply NO with reply_to_seat and a captain moves them to the drop-out row.\n\nMCP tool: `withdraw_from_seat`. Needs a read-write token.","tags":["Members"],"parameters":[{"name":"seatId","in":"path","required":true,"schema":{"type":"string","description":"Seat id of the caller's own sub / erg slot.","minLength":1,"maxLength":64}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/clubs/{club}/open-slots":{"get":{"operationId":"list_open_slots","summary":"List open slots","description":"Published sessions from today on (between `from` and `to`, at most 62 days) that the caller could still sign up for, with the number of free sub and erg slots.\n\nMCP tool: `list_open_slots`.","tags":["Members"],"parameters":[{"name":"club","in":"path","required":true,"schema":{"type":"string","description":"Club slug, e.g. \"lccbc\". `whoami` lists the clubs you belong to.","minLength":1,"maxLength":40}},{"name":"from","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","format":"date","description":"First day (default: today) as a Europe/London calendar day, \"YYYY-MM-DD\"."}},{"name":"to","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","format":"date","description":"Last day (default: 13 days after `from`) as a Europe/London calendar day, \"YYYY-MM-DD\"."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/clubs/{club}/availability":{"get":{"operationId":"get_availability","summary":"Get my availability","description":"The caller's own availability per day between two days (at most 62): morning and afternoon each available | maybe | unavailable, with an optional note, or null when not set. Captains use it to build crews.\n\nMCP tool: `get_availability`.","tags":["Members"],"parameters":[{"name":"club","in":"path","required":true,"schema":{"type":"string","description":"Club slug, e.g. \"lccbc\". `whoami` lists the clubs you belong to.","minLength":1,"maxLength":40}},{"name":"from","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","format":"date","description":"First day (default: today) as a Europe/London calendar day, \"YYYY-MM-DD\"."}},{"name":"to","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","format":"date","description":"Last day (default: 13 days after `from`) as a Europe/London calendar day, \"YYYY-MM-DD\"."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"operationId":"set_availability","summary":"Set my availability","description":"Saves the caller's availability for up to 62 days (today up to 120 days ahead). Each day replaces what was there. Morning = before about 12:00, afternoon = after. Confirm with the person before changing it.\n\nMCP tool: `set_availability`. Needs a read-write token.","tags":["Members"],"parameters":[{"name":"club","in":"path","required":true,"schema":{"type":"string","description":"Club slug, e.g. \"lccbc\". `whoami` lists the clubs you belong to.","minLength":1,"maxLength":40}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"days":{"type":"array","maxItems":62,"items":{"type":"object","properties":{"date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","format":"date","description":"Day as a Europe/London calendar day, \"YYYY-MM-DD\"."},"morning":{"type":"string","enum":["available","maybe","unavailable"]},"afternoon":{"type":"string","enum":["available","maybe","unavailable"]},"note":{"type":"string","description":"Optional note, e.g. 'free after 10'.","maxLength":200}},"required":["date","morning","afternoon"],"additionalProperties":false}}},"required":["days"],"additionalProperties":false}}}}}},"/clubs/{club}/key-dates":{"get":{"operationId":"list_key_dates","summary":"List key dates","description":"The club's key dates: races, Head-to-Heads, socials and deadlines, with date (or the original free-text date), entry deadline, location, organiser, course, link and the squads concerned.\n\nMCP tool: `list_key_dates`.","tags":["Members"],"parameters":[{"name":"club","in":"path","required":true,"schema":{"type":"string","description":"Club slug, e.g. \"lccbc\". `whoami` lists the clubs you belong to.","minLength":1,"maxLength":40}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/clubs/{club}/squads":{"get":{"operationId":"list_squads","summary":"List squads","description":"The club's squads (id, code, name, boat size, coxed, sub / erg slot counts, auto-publish rule) and the caller's role in each. Use it to turn a squad code into an id.\n\nMCP tool: `list_squads`.","tags":["Members"],"parameters":[{"name":"club","in":"path","required":true,"schema":{"type":"string","description":"Club slug, e.g. \"lccbc\". `whoami` lists the clubs you belong to.","minLength":1,"maxLength":40}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/conditions":{"get":{"operationId":"river_conditions","summary":"River conditions","description":"Current CUCBC flag on the Cam (with what it means for boating), the river level at the club's gauge, and today's and tomorrow's lighting-down / lighting-up times and weather. `stale` says when a source has not updated recently. No membership needed.\n\nMCP tool: `river_conditions`.","tags":["Members"],"parameters":[{"name":"club","in":"query","required":false,"schema":{"type":"string","description":"Optional club slug (picks its river gauge).","minLength":1,"maxLength":40}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"security":[{},{"bearerAuth":[]}]}},"/squads/{squadId}/sessions":{"post":{"operationId":"create_session","summary":"Create a session (captains)","description":"Creates a DRAFT outing or land session for a squad, with the squad's default empty seats. Drafts are visible only to that squad's captains and coaches until published with publish_week. Returns the new session in full.\n\nMCP tool: `create_session`. Needs a read-write token.","tags":["Captains"],"parameters":[{"name":"squadId","in":"path","required":true,"schema":{"type":"string","description":"Squad id (see whoami or list_squads).","minLength":1,"maxLength":64}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"kind":{"type":"string","enum":["outing","land"]},"date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","format":"date","description":"Session day as a Europe/London calendar day, \"YYYY-MM-DD\"."},"startTime":{"type":"string","pattern":"^\\d{2}:\\d{2}$","description":"Start (meet) time, 24 h London time \"HH:MM\". Leave out when the time is not known yet (TBC)."},"durationMinutes":{"type":"integer","minimum":5,"maximum":720,"description":"Length in minutes. Default: the club's outing / land length."},"timeNote":{"type":"string","description":"Free-text time when it is not a clean time, e.g. \"after lectures\".","maxLength":80},"title":{"type":"string","description":"Headline of the plan, e.g. \"Steady state + starts\" or \"Ergs: 3x2k\".","maxLength":120},"plan":{"type":"string","description":"The plan for the session (outing plan or land workout).","maxLength":2000},"boatId":{"type":"string","description":"Boat id (outings).","minLength":1,"maxLength":64},"boathouseId":{"type":"string","description":"Boathouse id (outings). Defaults to the boat's boathouse.","minLength":1,"maxLength":64},"location":{"type":"string","description":"Venue for land sessions, e.g. a gym.","maxLength":120},"coach":{"type":"string","description":"Coach name (free text; coaches are often not members).","maxLength":80},"notes":{"type":"string","description":"Notes shown to the crew.","maxLength":2000}},"required":["kind","date"],"additionalProperties":false}}}}}},"/sessions/{sessionId}/cancel":{"post":{"operationId":"cancel_session","summary":"Cancel a session (captains)","description":"Cancels a session (it stays visible, struck out) and tells everyone in it. Confirm with the person first.\n\nMCP tool: `cancel_session`. Needs a read-write token.","tags":["Captains"],"parameters":[{"name":"sessionId","in":"path","required":true,"schema":{"type":"string","description":"Session id.","minLength":1,"maxLength":64}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","description":"Why, e.g. \"Red flag\".","maxLength":300}},"additionalProperties":false}}}}}},"/seats/{seatId}/assign":{"post":{"operationId":"assign_seat","summary":"Assign a seat (captains)","description":"Puts a member (membershipId, from list_members or squad_planner) or an unclaimed name from the old sheet (placeholderId) into a seat. Pass neither to empty the seat. The person is emailed when the session is published.\n\nMCP tool: `assign_seat`. Needs a read-write token.","tags":["Captains"],"parameters":[{"name":"seatId","in":"path","required":true,"schema":{"type":"string","description":"Seat id (from get_session).","minLength":1,"maxLength":64}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"membershipId":{"type":"string","description":"Member to seat.","minLength":1,"maxLength":64},"placeholderId":{"type":"string","description":"Unclaimed sheet name to seat.","minLength":1,"maxLength":64}},"additionalProperties":false}}}}}},"/squads/{squadId}/publish":{"post":{"operationId":"publish_week","summary":"Publish drafts (captains)","description":"Publishes every draft of a squad between `from` and `to` (default: the Monday–Sunday week containing `from`, or this week) and emails the crews. Returns the WhatsApp announcement text. Confirm with the person first: this notifies the whole squad.\n\nMCP tool: `publish_week`. Needs a read-write token.","tags":["Captains"],"parameters":[{"name":"squadId","in":"path","required":true,"schema":{"type":"string","description":"Squad id.","minLength":1,"maxLength":64}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"from":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","format":"date","description":"First day (default: Monday of this week) as a Europe/London calendar day, \"YYYY-MM-DD\"."},"to":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","format":"date","description":"Last day (default: Sunday after `from`) as a Europe/London calendar day, \"YYYY-MM-DD\"."}},"additionalProperties":false}}}}}},"/squads/{squadId}/planner":{"get":{"operationId":"squad_planner","summary":"Squad planner (captains, coaches)","description":"The planning grid for one squad and one week (Monday–Sunday): the week's sessions, and every squad member with their availability per day (morning / afternoon) and the seats they already hold that day in any squad.\n\nMCP tool: `squad_planner`.","tags":["Captains"],"parameters":[{"name":"squadId","in":"path","required":true,"schema":{"type":"string","description":"Squad id.","minLength":1,"maxLength":64}},{"name":"week","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","format":"date","description":"Any day in the week (default: today) as a Europe/London calendar day, \"YYYY-MM-DD\"."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/clubs/{club}/members":{"get":{"operationId":"list_members","summary":"List members","description":"Active members of the club (or of one squad): membership id, display name, side, can cox / row, admin flag, committee titles and squads with roles. Emails appear only where the caller may see them (captains of their squads, admins). Captains may include pending and former members.\n\nMCP tool: `list_members`.","tags":["Captains"],"parameters":[{"name":"club","in":"path","required":true,"schema":{"type":"string","description":"Club slug, e.g. \"lccbc\". `whoami` lists the clubs you belong to.","minLength":1,"maxLength":40}},{"name":"squad","in":"query","required":false,"schema":{"type":"string","description":"Only this squad: squad id or code.","minLength":1,"maxLength":64}},{"name":"includeInactive","in":"query","required":false,"schema":{"type":"boolean","description":"Also pending, rejected and former members (captains)."}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/clubs/{club}/join-requests":{"get":{"operationId":"list_join_requests","summary":"List join requests (captains)","description":"Open requests to join the club that the caller may decide: name, email, requested squad, side, can cox, message and when they asked.\n\nMCP tool: `list_join_requests`.","tags":["Captains"],"parameters":[{"name":"club","in":"path","required":true,"schema":{"type":"string","description":"Club slug, e.g. \"lccbc\". `whoami` lists the clubs you belong to.","minLength":1,"maxLength":40}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/memberships/{membershipId}/approve":{"post":{"operationId":"approve_member","summary":"Approve a join request (captains)","description":"Lets a person into the club, into their requested squad (or `squadId`) with `role` (default member). They get an email. Captains can approve only into squads they captain.\n\nMCP tool: `approve_member`. Needs a read-write token.","tags":["Captains"],"parameters":[{"name":"membershipId","in":"path","required":true,"schema":{"type":"string","description":"Membership id from list_join_requests.","minLength":1,"maxLength":64}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"squadId":{"type":"string","description":"Squad to put them in (default: the one they asked for).","minLength":1,"maxLength":64},"role":{"type":"string","enum":["member","coach","captain"],"description":"Default member."}},"additionalProperties":false}}}}}},"/memberships/{membershipId}/reject":{"post":{"operationId":"reject_member","summary":"Decline a join request (captains)","description":"Declines an open join request. The person is told by email. Confirm with the person first.\n\nMCP tool: `reject_member`. Needs a read-write token.","tags":["Captains"],"parameters":[{"name":"membershipId","in":"path","required":true,"schema":{"type":"string","description":"Membership id from list_join_requests.","minLength":1,"maxLength":64}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Result"}}}},"400":{"description":"Invalid input","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing, unknown, expired or revoked token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Not allowed (not a member, not a captain, or a read-only token)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No such club, session, seat, squad or membership","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflict with the current state (e.g. replies closed, session cancelled)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Too many requests","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"cb_ + 40 base62 characters","description":"Personal API token from Settings. Shown once when created."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","enum":["unauthenticated","forbidden","not_found","invalid","conflict","rate_limited","internal"]},"message":{"type":"string","description":"Human-readable; safe to show to the person."}},"required":["code","message"]}},"required":["error"]},"Result":{"description":"The operation's result. Lists are JSON arrays; single things are objects. Field names match the MCP tool output."}}}}