{"openapi":"3.1.0","info":{"title":"Kicksmash API","version":"1.0.0","summary":"Kicksmash is the open, agent-native way to organise padel: create a match, share one link, and let people or their assistants join, all through an API that anyone may use.","description":"Reads need no key. Writes work without a key with a daily allowance per address; a free key (POST /api/v1/keys, issued instantly to people and assistants alike) raises it and unlocks webhooks. Public data is CC BY 4.0, except free court times read from a booking platform (freeCourts.source platform), which are not ours to license; the code is Apache-2.0. An MCP server with the same capabilities lives at /mcp.","license":{"name":"CC BY 4.0 (data), Apache-2.0 (code)","url":"https://github.com/evhg/padel-matchup/blob/main/LICENSE"},"contact":{"url":"https://kicksma.sh/developers"}},"servers":[{"url":"https://kicksma.sh"}],"externalDocs":{"description":"Developer guide, MCP server and agent charter","url":"https://kicksma.sh/developers"},"tags":[{"name":"matches","description":"Create, read and join matches and tournaments"},{"name":"venues","description":"Public venue boards"},{"name":"clubs","description":"Club pages: booking links, free courts, founding clubs"},{"name":"coaches","description":"Listed coaches: free times, becoming a student, booking and cancelling lessons under the coach's rules"},{"name":"series","description":"Opens that repeat: a tournament series with its rhythm, the next edition to sign up for, the past podiums"},{"name":"groups","description":"Crews that play together"},{"name":"schedules","description":"Americano rotations, no data stored"},{"name":"keys","description":"Optional keys for roomier limits and webhooks"},{"name":"webhooks","description":"Signed callbacks on match events"}],"paths":{"/api/v1/matches/{code}":{"get":{"tags":["matches"],"operationId":"getMatch","summary":"A match by its 4-character code","parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string","minLength":4,"maxLength":4}}],"responses":{"200":{"description":"The match","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Match"}}}},"404":{"description":"No such match","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/matches":{"post":{"tags":["matches"],"operationId":"createMatch","summary":"Create a match or an americano tournament","description":"Returns the public match plus the organizer's personal token, personal link and private manage link. Give those to the person you act for; never publish them.","security":[{},{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMatch"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMatchResult"}}}},"422":{"description":"Invalid request; the message names the field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited; the hint says how to get more room","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/matches/{code}/join":{"post":{"tags":["matches"],"operationId":"joinMatch","summary":"Join a match by name (or by an existing personal token)","description":"Outcomes: joined, waitlisted, already_in, full, or requested when the player's level is outside the match's range, or declared but unconfirmed on a match that takes confirmed levels only (the organizer approves on the match page; a coach's or the club's confirmation seats the player). A player who already played a finished, scored match with the organizer is never held by that organizer's range. A place the organizer holds for the same name (\"Danny\") refuses the join with 409 name_held, and nothing is written: if it is that person, they take the place on the match page; if not, they join with a fuller name, such as a last name. A name given without a token that a player in the match (seated or waiting) already carries refuses the join with 409 name_seated, and nothing is written: if it is that player, call again with their personal token; if not, join with a fuller name.","security":[{},{"bearer":[]}],"parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinMatch"}}}},"responses":{"200":{"description":"Result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JoinMatchResult"}}}},"404":{"description":"No such match","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A place is held for this name (name_held), a player in the match has this name (name_seated), or the match is over or called off","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid request; the message names the field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited; the hint says how to get more room","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/boards/{slug}":{"get":{"tags":["venues"],"operationId":"getBoard","summary":"Open, organizer-listed matches at a venue","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"},"description":"Lower-case venue name with dashes, e.g. padel-indoor-bcn"}],"responses":{"200":{"description":"The board","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Board"}}}},"404":{"description":"Unknown venue","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/series":{"get":{"tags":["series"],"operationId":"listSeries","summary":"Active series, optionally in one city, each with its next edition","parameters":[{"name":"city","in":"query","schema":{"type":"string"},"description":"phuket or singapore"}],"responses":{"200":{"description":"Series","content":{"application/json":{"schema":{"type":"object","properties":{"city":{"type":["string","null"]},"series":{"type":"array","items":{"$ref":"#/components/schemas/Series"}},"cities":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"}}}},"note":{"type":"string"}}}}}}}}},"/api/v1/series/{slug}":{"get":{"tags":["series"],"operationId":"getSeries","summary":"One series: rhythm, next edition, past editions with podiums","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The series","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SeriesPage"}}}},"404":{"description":"No such series","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/clubs":{"get":{"tags":["clubs"],"operationId":"listClubs","summary":"Live club pages, optionally for one city","parameters":[{"name":"city","in":"query","schema":{"type":"string"},"description":"phuket or singapore"}],"responses":{"200":{"description":"Clubs","content":{"application/json":{"schema":{"type":"object","properties":{"city":{"type":["string","null"]},"clubs":{"type":"array","items":{"$ref":"#/components/schemas/Club"}},"cities":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"}}}}}}}}}}}},"/api/v1/clubs/{slug}":{"get":{"tags":["clubs"],"operationId":"getClub","summary":"A club page: booking link, courts, today's free courts from the club's own feed or read from its booking platform's public page (see freeCourts.source)","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The club","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Club"}}}},"404":{"description":"No live club page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/coaches":{"get":{"tags":["coaches"],"operationId":"listCoaches","summary":"Listed coaches, optionally in one city","parameters":[{"name":"city","in":"query","schema":{"type":"string"},"description":"phuket or singapore"}],"responses":{"200":{"description":"Coaches","content":{"application/json":{"schema":{"type":"object","properties":{"city":{"type":["string","null"]},"coaches":{"type":"array","items":{"$ref":"#/components/schemas/Coach"}},"cities":{"type":"array","items":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"}}}},"note":{"type":"string"}}}}}}}}},"/api/v1/coaches/{handle}":{"get":{"tags":["coaches"],"operationId":"getCoach","summary":"A coach with their next free starts","parameters":[{"name":"handle","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The coach","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Coach"}}}},"404":{"description":"No listed coach","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/coaches/{handle}/slots":{"get":{"tags":["coaches"],"operationId":"getCoachSlots","summary":"Free starts inside the coach's hours","parameters":[{"name":"handle","in":"path","required":true,"schema":{"type":"string"}},{"name":"days","in":"query","schema":{"type":"integer","minimum":1,"maximum":30},"description":"Default 14"}],"responses":{"200":{"description":"Slots","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CoachSlots"}}}},"404":{"description":"No listed coach","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/coaches/{handle}/requests":{"post":{"tags":["coaches"],"operationId":"requestCoach","summary":"Ask to become the coach's student","description":"By first name (a new player) or personal token. The coach accepts with one tap; the response's personalToken is what book_lesson needs afterwards. Keep it private to the student.","security":[{},{"bearer":[]}],"parameters":[{"name":"handle","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":40},"token":{"type":"string"}}}}}},"responses":{"201":{"description":"Status","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["requested","accepted","paused"]},"coach":{"$ref":"#/components/schemas/Coach"},"student":{"type":"object","properties":{"name":{"type":"string"},"personalToken":{"type":"string"},"personalUrl":{"type":"string"}}},"next":{"type":"string"}}}}}},"404":{"description":"No listed coach","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid request; the message names the field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited; the hint says how to get more room","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/coaches/{handle}/lessons":{"post":{"tags":["coaches"],"operationId":"bookLesson","summary":"Book a lesson at a free start (accepted students only)","security":[{},{"bearer":[]}],"parameters":[{"name":"handle","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"startsAt":{"type":"string","format":"date-time"}},"required":["token","startsAt"]}}}},"responses":{"201":{"description":"Booked","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LessonBooking"}}}},"403":{"description":"Not accepted yet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"No listed coach or unknown token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Slot taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid request; the message names the field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited; the hint says how to get more room","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/coaches/{handle}/lessons/{id}":{"delete":{"tags":["coaches"],"operationId":"cancelLesson","summary":"Cancel a lesson under the coach's rules","description":"The outcome says what happened to the package: refunded (in time), free_pass (late, covered), counted (late), none (no package).","security":[{},{"bearer":[]}],"parameters":[{"name":"handle","in":"path","required":true,"schema":{"type":"string"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"token","in":"query","schema":{"type":"string"},"description":"Or in the JSON body"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"}}}}}},"responses":{"200":{"description":"Cancelled","content":{"application/json":{"schema":{"type":"object","properties":{"lessonId":{"type":"string"},"outcome":{"type":"string","enum":["refunded","free_pass","counted","none"]},"next":{"type":"string"}}}}}},"404":{"description":"No such lesson for this token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid request; the message names the field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited; the hint says how to get more room","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/groups/{code}":{"get":{"tags":["groups"],"operationId":"getGroup","summary":"A group: members' first names, weekly slot, whether it asks to join, upcoming matches","parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string","minLength":6,"maxLength":6}}],"responses":{"200":{"description":"The group","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Group"}}}},"404":{"description":"No such group","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/schedule":{"get":{"tags":["schedules"],"operationId":"getSchedule","summary":"An americano rotation (exact when the field is in fours)","parameters":[{"name":"players","in":"query","schema":{"type":"integer","minimum":4,"maximum":64}},{"name":"names","in":"query","description":"Comma-separated names; overrides players","schema":{"type":"string"}},{"name":"courts","in":"query","schema":{"type":"integer","minimum":1,"maximum":16}},{"name":"rounds","in":"query","schema":{"type":"integer","minimum":1,"maximum":40}},{"name":"seed","in":"query","schema":{"type":"integer"}}],"responses":{"200":{"description":"The schedule","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Schedule"}}}},"422":{"description":"Invalid request; the message names the field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited; the hint says how to get more room","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["schedules"],"operationId":"postSchedule","summary":"Same as GET, with a JSON body","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleInput"}}}},"responses":{"200":{"description":"The schedule","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Schedule"}}}},"422":{"description":"Invalid request; the message names the field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited; the hint says how to get more room","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/keys":{"post":{"tags":["keys"],"operationId":"createKey","summary":"Get an API key instantly","description":"No approval, no email verification. The key is shown once. Say who or what will use it; an assistant may name itself.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","maxLength":80},"email":{"type":"string","format":"email"},"agent":{"type":"string","maxLength":80,"description":"e.g. \"claude\", \"chatgpt\", \"my-club-bot\""}},"required":["name"]}}}},"responses":{"201":{"description":"The key, once","content":{"application/json":{"schema":{"type":"object","properties":{"key":{"type":"string"},"prefix":{"type":"string"},"name":{"type":"string"},"limits":{"type":"object"},"next":{"type":"string"}}}}}},"422":{"description":"Invalid request; the message names the field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited; the hint says how to get more room","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/webhooks":{"get":{"tags":["webhooks"],"operationId":"listWebhooks","summary":"Your webhooks","security":[{"bearer":[]}],"responses":{"200":{"description":"Webhooks","content":{"application/json":{"schema":{"type":"object","properties":{"webhooks":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}}}}}}},"401":{"description":"Key required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["webhooks"],"operationId":"createWebhook","summary":"Subscribe to match events","security":[{"bearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string","enum":["match.created","match.updated","match.joined","match.left","match.full","match.cancelled","match.result"]}},"filter":{"type":"object","properties":{"venueSlug":{"type":"string"},"groupCode":{"type":"string"},"codes":{"type":"array","items":{"type":"string"}}}}},"required":["url"]}}}},"responses":{"201":{"description":"Created; the secret is shown once","content":{"application/json":{"schema":{"type":"object","properties":{"webhook":{"$ref":"#/components/schemas/Webhook"},"secret":{"type":"string"},"signing":{"type":"string"}}}}}},"401":{"description":"Key required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Invalid request; the message names the field","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limited; the hint says how to get more room","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/webhooks/{id}":{"delete":{"tags":["webhooks"],"operationId":"deleteWebhook","summary":"Remove a webhook","security":[{"bearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"204":{"description":"Removed"},"404":{"description":"Not yours or already gone","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"Optional for writes, required for webhooks. Keys start with ks_live_."}},"schemas":{"Coach":{"type":"object","properties":{"handle":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"},"bookUrl":{"type":"string"},"city":{"type":["string","null"]},"tz":{"type":"string"},"clubs":{"type":"array","items":{"type":"string"}},"lessonMinutes":{"type":"integer"},"languages":{"type":"array","items":{"type":"string"}},"bio":{"type":["string","null"]},"rules":{"type":"object","properties":{"cutoffHours":{"type":"integer"},"latePasses":{"type":"integer"},"minNoticeHours":{"type":"integer"}}},"nextSlots":{"type":"array","items":{"type":"string","format":"date-time"}}}},"CoachSlots":{"type":"object","properties":{"coach":{"$ref":"#/components/schemas/Coach"},"days":{"type":"integer"},"slots":{"type":"array","items":{"type":"string","format":"date-time"}},"rules":{"type":"object"},"next":{"type":"string"}}},"LessonBooking":{"type":"object","properties":{"lesson":{"type":"object","properties":{"id":{"type":"string"},"startsAt":{"type":"string","format":"date-time"},"minutes":{"type":"integer"},"coach":{"type":"string"},"url":{"type":"string"}}},"package":{"type":["object","null"],"properties":{"left":{"type":"integer"},"size":{"type":"integer"},"expiresAt":{"type":["string","null"]}}},"rules":{"type":"object"},"next":{"type":"string"}}},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"hint":{"type":"string"},"status":{"type":"integer"},"docs":{"type":"string"}},"required":["code","message","status"]}},"required":["error"]},"Match":{"type":"object","description":"Exactly what the public match page shows. First names and levels only; never emails, phones, tokens or manage links.","properties":{"code":{"type":"string"},"url":{"type":"string"},"type":{"type":"string","enum":["match","tournament"]},"format":{"type":["string","null"],"enum":["americano","mexicano","king",null],"description":"Tournaments only."},"fixedPairs":{"type":["boolean","null"],"description":"Tournaments only: two partners play every round together instead of rotating."},"pairs":{"type":["array","null"],"description":"A fixed-pairs tournament's list as pairs, in order: two first names each, or one name that needs a partner. Null otherwise.","items":{"type":"object","properties":{"names":{"type":"array","items":{"type":"string"}},"partnerNeeded":{"type":"boolean"}}}},"title":{"type":["string","null"]},"status":{"type":"string","enum":["open","full","cancelled","past"]},"startsAt":{"type":"string","format":"date-time"},"endsAt":{"type":"string","format":"date-time","description":"startsAt plus durationMinutes."},"durationMinutes":{"type":"integer","enum":[60,90,120],"description":"How long the organiser booked the court, in minutes. The calendar invitation ends here too."},"tz":{"type":"string"},"venue":{"type":["object","null"],"properties":{"name":{"type":"string"},"slug":{"type":["string","null"]},"mapUrl":{"type":["string","null"]},"court":{"type":["string","null"]},"boardUrl":{"type":["string","null"]}}},"capacity":{"type":"integer"},"players":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"level":{"type":["number","null"]},"organizer":{"type":"boolean"},"status":{"type":"string","enum":["joined","confirmed","invited"]}}}},"spotsLeft":{"type":"integer"},"waitlist":{"type":"integer"},"whenFull":{"type":"string","enum":["waitlist","closed"]},"level":{"type":["object","null"],"properties":{"min":{"type":["number","null"]},"max":{"type":["number","null"]},"preset":{"type":["string","null"],"description":"bronze, silver, gold, platinum or custom"},"verifiedOnly":{"type":"boolean","description":"Only levels confirmed by an organizer, a coach or the club walk in; declared levels inside the range ask to join."}}},"category":{"type":["string","null"],"enum":["men","women","mixed",null],"description":"Who the match is for; null for anyone. Information only: nobody is checked when they join."},"ageMin":{"type":["integer","null"],"enum":[35,45,55,null],"description":"An age tag, read as 35+, 45+ or 55+; null for any age. Information only."},"group":{"type":["object","null"],"properties":{"code":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"}}},"listed":{"type":"boolean"},"bookingUrl":{"type":["string","null"]},"courtBooked":{"type":["object","null"],"description":"A player booked the court in the club's own app and said so. Null while not booked and on a cancelled match. Kicksmash never books or takes a payment.","properties":{"at":{"type":"string","format":"date-time"},"by":{"type":["string","null"],"description":"The booker's first name; null when the booker no longer holds a seat or their account is gone."}}},"cost":{"type":["string","null"],"description":"What each player pays, free text."},"note":{"type":["string","null"]},"result":{"type":["object","null"],"properties":{"sets":{"type":"array","items":{"type":"object","properties":{"a":{"type":"integer"},"b":{"type":"integer"}}}},"teamA":{"type":"array","items":{"type":"string"}},"teamB":{"type":"array","items":{"type":"string"}},"winner":{"type":"string","enum":["a","b","draw"],"description":"Sets can be empty when only the winner was recorded."},"confirmed":{"type":"boolean"}}},"createdAt":{"type":"string","format":"date-time"}},"required":["code","url","type","status","startsAt","tz","capacity","players","spotsLeft"]},"CreateMatch":{"type":"object","properties":{"type":{"default":"match","description":"A match is exactly four players. A tournament is an americano with 4 to 64 players in fours.","type":"string","enum":["match","tournament"]},"startsAt":{"type":"string","minLength":10,"maxLength":40,"description":"ISO 8601 date-time. With an offset or Z it is absolute; without one it is read in tz. Example: 2026-09-11T19:00"},"tz":{"type":"string","minLength":1,"maxLength":64,"description":"IANA time zone the players live in, for example Asia/Singapore or Asia/Bangkok."},"durationMinutes":{"description":"How long the court is booked: 60, 90 or 120 minutes. Omit for 90 (a match) or 120 (a tournament). The match page, the cards and the calendar invitation show it.","type":"number","enum":[60,90,120]},"venue":{"description":"Club or court name. Enables the venue board and the booking link.","type":"string","maxLength":80},"venueMapUrl":{"type":"string","maxLength":500,"format":"uri"},"court":{"description":"Court within the venue, e.g. \"3\".","type":"string","maxLength":40},"capacity":{"description":"Tournaments only, a multiple of 4. Matches are always 4.","type":"integer","minimum":4,"maximum":64},"format":{"description":"Tournaments only. americano: partners rotate, everyone plays everyone. mexicano: courts by standings after round 1. king: winners move up a court, losers down.","type":"string","enum":["americano","mexicano","king"]},"fixedPairs":{"default":false,"description":"Tournaments only: two partners play every round together, in any format. Pairs sign up together (join_match with partner); a player alone is listed as needing a partner. Americano then rotates the opponents: every pair meets every other pair, and the table ranks pairs.","type":"boolean"},"pointsPerMatch":{"description":"Tournaments only: fixed points per match (16, 21, 24, 32). Omit for free scoring; mexicano defaults to 24.","type":"integer","minimum":4,"maximum":99},"gamesTo":{"description":"Tournaments only: first to N games (4, 6, 8) instead of points; the table then ranks by matches won.","type":"integer","minimum":2,"maximum":12},"whenFull":{"default":"waitlist","type":"string","enum":["waitlist","closed"]},"levelMin":{"description":"Level range 0 to 7 (Playtomic-style). Omit both for any level.","anyOf":[{"type":"number","minimum":0,"maximum":7},{"type":"null"}]},"levelMax":{"anyOf":[{"type":"number","minimum":0,"maximum":7},{"type":"null"}]},"verifiedLevelsOnly":{"default":false,"description":"With a range: only levels confirmed by an organizer, a coach or a club walk in; declared levels inside the range ask to join.","type":"boolean"},"category":{"description":"Who the match is for: men, women or mixed. Omit for anyone. Information for the players who find it; nobody is checked when they join.","anyOf":[{"type":"string","enum":["men","women","mixed"]},{"type":"null"}]},"ageMin":{"description":"An age tag: 35, 45 or 55 means 35+, 45+ or 55+. Omit for any age. Information only; nobody is checked when they join.","anyOf":[{"type":"number","enum":[35,45,55]},{"type":"null"}]},"title":{"type":"string","maxLength":80},"note":{"type":"string","maxLength":500},"bookingUrl":{"description":"The club's booking page or confirmation link, shown to players.","type":"string","maxLength":500,"format":"uri"},"cost":{"description":"What each player pays, as free text: 400 THB, €8, split 4 ways.","type":"string","maxLength":40},"listOnVenueBoard":{"default":false,"description":"Show the match on the public venue board (/v/{venue-slug}). Off by default.","type":"boolean"},"organizer":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":40,"description":"First name is enough."},"token":{"description":"Personal token of an existing Kicksmash player, returned by earlier calls, so the same person organizes again.","type":"string","minLength":8,"maxLength":64},"email":{"description":"Optional. Gets the organizer link and calendar invite by email.","type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"level":{"type":"number","minimum":0,"maximum":7}},"required":["name"]},"organizerPlays":{"default":true,"description":"Seat the organizer in the match (default). False when they only organize.","type":"boolean"}},"required":["startsAt","tz","organizer"]},"JoinMatch":{"type":"object","properties":{"code":{"type":"string","minLength":4,"maxLength":4,"description":"The 4-character match code from the link, e.g. kicksma.sh/AB12 → AB12."},"name":{"description":"Required unless token is given.","type":"string","minLength":1,"maxLength":40},"token":{"description":"Personal token of an existing player.","type":"string","minLength":8,"maxLength":64},"email":{"description":"Optional. Sends a calendar invite that updates itself.","type":"string","format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"level":{"description":"Needed once when the match has a level range and the player has no level yet.","type":"number","minimum":0,"maximum":7},"partner":{"description":"A fixed-pairs tournament only: the partner's first name. Their spot is reserved with its own invite link (partner.inviteUrl in the answer), which the partner opens to claim it. Omit to join alone: the player is listed as needing a partner. A player already in alone may call again with a partner.","type":"string","minLength":1,"maxLength":40}},"required":["code"]},"ScheduleInput":{"type":"object","properties":{"players":{"description":"Number of players; ignored when names are given.","type":"integer","minimum":4,"maximum":64},"names":{"minItems":4,"maxItems":64,"type":"array","items":{"type":"string","minLength":1,"maxLength":40}},"courts":{"description":"Defaults to floor(players / 4).","type":"integer","minimum":1,"maximum":16},"rounds":{"description":"Defaults to players − 1 when the field is in fours (every pair partners once), else players.","type":"integer","minimum":1,"maximum":40},"format":{"default":"americano","type":"string","enum":["americano"]},"seed":{"description":"Same seed, same schedule.","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}}},"CreateMatchResult":{"type":"object","properties":{"match":{"$ref":"#/components/schemas/Match"},"organizer":{"type":"object","properties":{"name":{"type":"string"},"personalToken":{"type":"string"},"personalUrl":{"type":"string"},"manageUrl":{"type":"string"}}},"shareUrl":{"type":"string"},"next":{"type":"string"}}},"JoinMatchResult":{"type":"object","properties":{"outcome":{"type":"string","enum":["joined","waitlisted","already_in","full","requested"]},"match":{"$ref":"#/components/schemas/Match"},"player":{"type":"object","properties":{"name":{"type":"string"},"personalToken":{"type":"string"},"personalUrl":{"type":"string"}}},"partner":{"type":["object","null"],"description":"A fixed-pairs tournament joined with a partner: the partner's name and the link that claims their reserved spot. Private to whoever named them.","properties":{"name":{"type":"string"},"inviteUrl":{"type":"string"}}},"group":{"type":["object","null"],"description":"The match's group, and whether the player is a member now. A seat never makes a member of a group that asks to join: member stays false and the player asks on the group page.","properties":{"code":{"type":"string"},"url":{"type":"string"},"askToJoin":{"type":"boolean"},"member":{"type":"boolean"}}},"next":{"type":"string"}}},"Series":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"},"organizer":{"type":["string","null"]},"format":{"type":"string","enum":["americano","mexicano","king"]},"rhythm":{"type":"object","properties":{"every":{"type":"string","enum":["week","fortnight","month"]},"weekday":{"type":"integer","description":"0 = Sunday"},"time":{"type":"string","description":"HH:MM in tz"},"nth":{"type":["integer","null"],"description":"Monthly: which weekday of the month, 5 = the last"},"tz":{"type":"string"}}},"venue":{"type":["object","null"],"properties":{"name":{"type":"string"},"slug":{"type":["string","null"]},"mapUrl":{"type":["string","null"]}}},"level":{"type":"object","properties":{"min":{"type":["number","null"]},"max":{"type":["number","null"]},"verifiedOnly":{"type":"boolean"}}},"category":{"type":["string","null"],"enum":["men","women","mixed",null],"description":"Who the match is for; null for anyone. Information only: nobody is checked when they join."},"ageMin":{"type":["integer","null"],"enum":[35,45,55,null],"description":"An age tag, read as 35+, 45+ or 55+; null for any age. Information only."},"capacity":{"type":"integer"},"cost":{"type":["string","null"]},"active":{"type":"boolean"},"next":{"type":["object","null"],"properties":{"code":{"type":"string"},"url":{"type":"string"},"startsAt":{"type":"string","format":"date-time"},"spotsLeft":{"type":"integer"}}}}},"SeriesPage":{"allOf":[{"$ref":"#/components/schemas/Series"},{"type":"object","properties":{"editions":{"type":"integer"},"past":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"url":{"type":"string"},"startsAt":{"type":"string","format":"date-time"},"podium":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"rank":{"type":"integer"}}}}}}}}}]},"Club":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"},"city":{"type":["string","null"]},"country":{"type":["string","null"],"description":"ISO 3166-1 alpha-2"},"province":{"type":["string","null"],"description":"The place as the club named it"},"mapUrl":{"type":["string","null"]},"website":{"type":["string","null"]},"booking":{"type":["object","null"],"properties":{"url":{"type":"string"},"platform":{"type":["string","null"],"description":"playtomic, matchi, playbypoint, … or null for the club's own page"},"platformName":{"type":["string","null"]}}},"courts":{"type":["integer","null"]},"courtsIndoor":{"type":["integer","null"],"description":"Indoor courts of the total; null when the club has not said, 0 when none"},"courtsOutdoor":{"type":["integer","null"]},"courtNames":{"type":"array","items":{"type":"string"},"description":"The courts by name, as the club listed them; empty until it does"},"about":{"type":["string","null"]},"founding":{"type":"boolean"},"freeCourts":{"type":["object","null"],"description":"Today's free courts still to come, in the club's zone; null when there is nothing current to say (no feed and no recent read, or the read failed or is more than two hours old). From the club's own feed (source club), or read from its booking platform's public page (source platform), up to every 15 minutes. free is the number of courts free for the whole slot; a platform's slots never overlap. Times read from a platform are not under the CC BY 4.0 licence.","properties":{"day":{"type":"string"},"tz":{"type":"string"},"fetchedAt":{"type":"string"},"source":{"type":"string","enum":["club","platform"],"description":"club: a feed the club shares; platform: read from the booking platform's public page"},"platform":{"type":["string","null"],"description":"The platform read, as in booking.platform (playtomic, matchi, …); null for a club's own feed"},"slots":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"},"free":{"type":"integer"}}}}}},"boardUrl":{"type":"string"},"rankingUrl":{"type":"string"},"calendarUrl":{"type":"string"}}},"Board":{"type":"object","properties":{"slug":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"},"mapUrl":{"type":["string","null"]},"calendarUrl":{"type":"string"},"matches":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"url":{"type":"string"},"type":{"type":"string"},"title":{"type":["string","null"]},"startsAt":{"type":"string"},"tz":{"type":"string"},"capacity":{"type":"integer"},"players":{"type":"integer"},"spotsLeft":{"type":"integer"},"level":{"type":["object","null"],"properties":{"min":{"type":["number","null"]},"max":{"type":["number","null"]},"preset":{"type":["string","null"],"description":"bronze, silver, gold, platinum or custom"},"verifiedOnly":{"type":"boolean","description":"Only levels confirmed by an organizer, a coach or the club walk in; declared levels inside the range ask to join."}}},"category":{"type":["string","null"],"enum":["men","women","mixed",null],"description":"Who the match is for; null for anyone. Information only: nobody is checked when they join."},"ageMin":{"type":["integer","null"],"enum":[35,45,55,null],"description":"An age tag, read as 35+, 45+ or 55+; null for any age. Information only."}}}}}},"Group":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"url":{"type":"string"},"calendarUrl":{"type":"string"},"venue":{"type":"object"},"tz":{"type":"string"},"type":{"type":"string"},"capacity":{"type":"integer"},"level":{"type":["object","null"],"properties":{"min":{"type":["number","null"]},"max":{"type":["number","null"]},"preset":{"type":["string","null"],"description":"bronze, silver, gold, platinum or custom"},"verifiedOnly":{"type":"boolean","description":"Only levels confirmed by an organizer, a coach or the club walk in; declared levels inside the range ask to join."}}},"weekly":{"type":["object","null"],"properties":{"weekday":{"type":"integer","description":"0 = Sunday"},"time":{"type":"string"},"leadDays":{"type":"integer"}}},"askToJoin":{"type":"boolean","description":"New people ask to join and an admin approves, on the group page. Off: anyone with the link joins."},"memberCount":{"type":"integer"},"members":{"type":"array","description":"First names. A member's level is for the group's own members, so it is always null here.","items":{"type":"object","properties":{"name":{"type":"string"},"level":{"type":["number","null"],"description":"Always null in public output."},"admin":{"type":"boolean"}}}},"upcoming":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"url":{"type":"string"},"startsAt":{"type":"string"},"title":{"type":["string","null"]},"category":{"type":["string","null"],"enum":["men","women","mixed",null],"description":"Who the match is for; null for anyone. Information only: nobody is checked when they join."},"ageMin":{"type":["integer","null"],"enum":[35,45,55,null],"description":"An age tag, read as 35+, 45+ or 55+; null for any age. Information only."}}}}}},"Schedule":{"type":"object","properties":{"format":{"type":"string"},"players":{"type":"integer"},"courts":{"type":"integer"},"exact":{"type":"boolean"},"rounds":{"type":"array","items":{"type":"object","properties":{"round":{"type":"integer"},"matches":{"type":"array","items":{"type":"object","properties":{"court":{"type":"integer"},"a":{"type":"array","items":{"type":"string"}},"b":{"type":"array","items":{"type":"string"}}}}},"resting":{"type":"array","items":{"type":"string"}}}}},"note":{"type":"string"}}},"Webhook":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"events":{"type":"array","items":{"type":"string"}},"filter":{"type":["object","null"]},"createdAt":{"type":"string"}}}}}}