{
  "openapi": "3.1.1",
  "info": {
    "title": "Padel & Pickleball Scheduler API",
    "version": "1.0.0",
    "summary": "Fair Americano or Mexicano doubles rounds with balanced sit-outs, scores and standings.",
    "description": "Every endpoint performs the same calculation as the human page and returns inputs, result, units, assumptions, sources, warnings and computedAt. MCP endpoint: /mcp."
  },
  "servers": [
    {
      "url": "https://padelpickleballscheduler.usefulweb.site"
    }
  ],
  "paths": {
    "/api/v1/calculate": {
      "post": {
        "operationId": "calculate",
        "summary": "Fair Americano or Mexicano doubles rounds with balanced sit-outs, scores and standings.",
        "description": "Enter the players, the number of courts and rounds. The scheduler creates doubles rounds that avoid repeating partners and opponents and spreads sit-outs evenly (Americano), or pairs players by their current standings (Mexicano: 1st + 4th against 2nd + 3rd). Enter scores to get the standings and the next Mexicano round.\n\nMethodology:\n- Each round uses min(courts, ⌊players ÷ 4⌋) courts; everyone else sits out. Sit-outs go to the players who have sat out least, then to those who sat out longest ago, then from the bottom of the list, so sit-outs differ by at most one where possible.\n- Americano: the active players are arranged on courts to minimise a penalty: 10 for every earlier round two partners already played together and 3 for every earlier meeting of two opponents. Starting from a rotated order, a deterministic local search swaps players while the penalty falls. With few players, repeats are unavoidable; the fairness table shows them.\n- Mexicano: round 1 is drawn like Americano from the list order. From round 2, the active players are ranked by the standings, and every group of four (1–4, 5–8 …) plays 1st + 4th against 2nd + 3rd. A round can only be drawn when all scores of the previous round are entered.\n- Standings: every player gets the points their team scored. Sorted by points, then wins, then point difference, then name.\n- No randomness: the same players and settings always give the same schedule.",
        "tags": [
          "fun"
        ],
        "x-mcp-tool": "padel_pickleball_scheduler",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PadelPickleballSchedulerInput"
              },
              "examples": {
                "example1": {
                  "summary": "Americano: 9 players, 2 courts, 6 rounds",
                  "value": {
                    "sport": "padel",
                    "format": "americano",
                    "players": [
                      "Ana",
                      "Ben",
                      "Carla",
                      "Dev",
                      "Eli",
                      "Fran",
                      "Gus",
                      "Hana",
                      "Ivo"
                    ],
                    "courts": 2,
                    "rounds": 6,
                    "pointsPerMatch": 24
                  }
                },
                "example2": {
                  "summary": "Mexicano: 8 players, scores for round 1",
                  "value": {
                    "sport": "padel",
                    "format": "mexicano",
                    "players": [
                      "P1",
                      "P2",
                      "P3",
                      "P4",
                      "P5",
                      "P6",
                      "P7",
                      "P8"
                    ],
                    "courts": 2,
                    "rounds": 3,
                    "pointsPerMatch": 24,
                    "scores": [
                      {
                        "round": 1,
                        "court": 1,
                        "scoreA": 15,
                        "scoreB": 9
                      },
                      {
                        "round": 1,
                        "court": 2,
                        "scoreA": 10,
                        "scoreB": 14
                      }
                    ]
                  }
                },
                "example3": {
                  "summary": "Pickleball mixer: 6 players, 1 court",
                  "value": {
                    "sport": "pickleball",
                    "format": "americano",
                    "players": [
                      "Kim",
                      "Lou",
                      "Max",
                      "Nia",
                      "Oli",
                      "Pat"
                    ],
                    "courts": 1,
                    "rounds": 6,
                    "pointsPerMatch": 0
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Calculation result envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PadelPickleballSchedulerResult"
                }
              }
            }
          },
          "400": {
            "description": "Malformed JSON body.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "422": {
            "description": "Invalid input (see errors[]).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded (see Retry-After).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/tool": {
      "get": {
        "operationId": "getToolDefinition",
        "summary": "Inputs, output schema and examples for this tool",
        "responses": {
          "200": {
            "description": "Tool definition.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Problem": {
        "type": "object",
        "description": "RFC 9457 problem details. 422 responses include per-field `errors`.",
        "required": [
          "type",
          "title",
          "status",
          "detail"
        ],
        "properties": {
          "type": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer"
          },
          "detail": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "field",
                "message"
              ],
              "properties": {
                "field": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "PadelPickleballSchedulerInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "sport": {
            "title": "Sport",
            "type": "string",
            "enum": [
              "padel",
              "pickleball"
            ],
            "default": "padel"
          },
          "format": {
            "title": "Format",
            "type": "string",
            "enum": [
              "americano",
              "mexicano"
            ],
            "default": "americano"
          },
          "players": {
            "title": "Players",
            "description": "One name per line, at least 4. For Mexicano, list them by strength for the first round.",
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 48
          },
          "courts": {
            "title": "Courts",
            "type": "integer",
            "minimum": 1,
            "maximum": 12,
            "default": 2
          },
          "rounds": {
            "title": "Rounds",
            "type": "integer",
            "minimum": 1,
            "maximum": 30,
            "default": 6
          },
          "pointsPerMatch": {
            "title": "Points per match",
            "description": "Americano/Mexicano usually play a fixed total (e.g. 24 or 32 points); pickleball games often go to 11. Used only to check scores; 0 = no check.",
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "default": 24
          },
          "scores": {
            "title": "Scores",
            "description": "Optional. Round, court and the points of team A (left) and team B (right) as shown in the schedule.",
            "type": "array",
            "maxItems": 200,
            "items": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "round": {
                  "title": "Round",
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 30
                },
                "court": {
                  "title": "Court",
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 12
                },
                "scoreA": {
                  "title": "Team A points",
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 999
                },
                "scoreB": {
                  "title": "Team B points",
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 999
                }
              },
              "required": [
                "round",
                "court",
                "scoreA",
                "scoreB"
              ]
            }
          }
        },
        "required": [
          "players"
        ]
      },
      "PadelPickleballSchedulerResult": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "schemaVersion",
          "tool",
          "computedAt",
          "inputs",
          "summary",
          "result",
          "units",
          "assumptions",
          "sources",
          "warnings",
          "presentation",
          "links"
        ],
        "properties": {
          "schemaVersion": {
            "type": "string",
            "const": "1.0"
          },
          "tool": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "id",
              "name",
              "version"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "version": {
                "type": "string"
              }
            }
          },
          "computedAt": {
            "type": "string",
            "format": "date-time"
          },
          "inputs": {
            "type": "object",
            "description": "Normalised inputs actually used, including applied defaults."
          },
          "summary": {
            "type": "string"
          },
          "result": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "format",
              "activeCourts",
              "roundsPlanned",
              "roundsRequested",
              "pendingRound",
              "rounds",
              "fairness",
              "standings"
            ],
            "properties": {
              "format": {
                "type": "string",
                "enum": [
                  "americano",
                  "mexicano"
                ]
              },
              "activeCourts": {
                "type": "integer"
              },
              "roundsPlanned": {
                "type": "integer"
              },
              "roundsRequested": {
                "type": "integer"
              },
              "pendingRound": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Mexicano: the first round that needs the previous round's scores before it can be drawn."
              },
              "rounds": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "round",
                    "matches",
                    "sittingOut"
                  ],
                  "properties": {
                    "round": {
                      "type": "integer"
                    },
                    "sittingOut": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "matches": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": false,
                        "required": [
                          "court",
                          "teamA",
                          "teamB",
                          "scoreA",
                          "scoreB"
                        ],
                        "properties": {
                          "court": {
                            "type": "integer"
                          },
                          "teamA": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "teamB": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "scoreA": {
                            "type": [
                              "integer",
                              "null"
                            ]
                          },
                          "scoreB": {
                            "type": [
                              "integer",
                              "null"
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              },
              "fairness": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "player",
                    "matches",
                    "sitOuts",
                    "distinctPartners",
                    "repeatedPartners",
                    "distinctOpponents"
                  ],
                  "properties": {
                    "player": {
                      "type": "string"
                    },
                    "matches": {
                      "type": "integer"
                    },
                    "sitOuts": {
                      "type": "integer"
                    },
                    "distinctPartners": {
                      "type": "integer"
                    },
                    "repeatedPartners": {
                      "type": "integer"
                    },
                    "distinctOpponents": {
                      "type": "integer"
                    }
                  }
                }
              },
              "standings": {
                "type": "array",
                "items": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "rank",
                    "player",
                    "points",
                    "pointsAgainst",
                    "diff",
                    "wins",
                    "draws",
                    "losses",
                    "matches"
                  ],
                  "properties": {
                    "rank": {
                      "type": "integer"
                    },
                    "player": {
                      "type": "string"
                    },
                    "points": {
                      "type": "integer"
                    },
                    "pointsAgainst": {
                      "type": "integer"
                    },
                    "diff": {
                      "type": "integer"
                    },
                    "wins": {
                      "type": "integer"
                    },
                    "draws": {
                      "type": "integer"
                    },
                    "losses": {
                      "type": "integer"
                    },
                    "matches": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "units": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "assumptions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": [
                "title"
              ],
              "properties": {
                "title": {
                  "type": "string"
                },
                "url": {
                  "type": "string"
                },
                "publisher": {
                  "type": "string"
                },
                "retrievedAt": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "presentation": {
            "type": "array",
            "items": {
              "type": "object"
            },
            "description": "Display blocks used by the human page (same data)."
          },
          "links": {
            "type": "object",
            "additionalProperties": false,
            "required": [
              "human",
              "api",
              "openapi",
              "methodology"
            ],
            "properties": {
              "human": {
                "type": "string",
                "format": "uri"
              },
              "api": {
                "type": "string",
                "format": "uri"
              },
              "openapi": {
                "type": "string",
                "format": "uri"
              },
              "methodology": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        }
      }
    }
  }
}