{
  "openapi": "3.0.1",
  "info": {
    "title": "TableHub API",
    "description": "Modular board-game platform — Classic Backgammon MVP",
    "version": "v1"
  },
  "paths": {
    "/health": {
      "get": {
        "tags": [
          "TableHub.Api"
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/": {
      "get": {
        "tags": [
          "TableHub.Api"
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/auth/register": {
      "post": {
        "tags": [
          "Auth"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/AuthResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/login": {
      "post": {
        "tags": [
          "Auth"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/AuthResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/refresh": {
      "post": {
        "tags": [
          "Auth"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RefreshRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/RefreshRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/RefreshRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/AuthResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/social": {
      "post": {
        "tags": [
          "Auth"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SocialLoginRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/SocialLoginRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/SocialLoginRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/AuthResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/competitions": {
      "get": {
        "tags": [
          "Competitions"
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "post": {
        "tags": [
          "Competitions"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCompetitionRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCompetitionRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCompetitionRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/competitions/{competitionId}": {
      "get": {
        "tags": [
          "Competitions"
        ],
        "parameters": [
          {
            "name": "competitionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/competitions/{competitionId}/play": {
      "post": {
        "tags": [
          "Competitions"
        ],
        "parameters": [
          {
            "name": "competitionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/competitions/{competitionId}/invite": {
      "post": {
        "tags": [
          "Competitions"
        ],
        "parameters": [
          {
            "name": "competitionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/competitions/invites/{inviteId}": {
      "get": {
        "tags": [
          "Competitions"
        ],
        "parameters": [
          {
            "name": "inviteId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "delete": {
        "tags": [
          "Competitions"
        ],
        "parameters": [
          {
            "name": "inviteId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/competitions/invites/join": {
      "post": {
        "tags": [
          "Competitions"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JoinCompetitionInviteRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/JoinCompetitionInviteRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/JoinCompetitionInviteRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/competitions/tickets/{ticketId}": {
      "get": {
        "tags": [
          "Competitions"
        ],
        "parameters": [
          {
            "name": "ticketId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "delete": {
        "tags": [
          "Competitions"
        ],
        "parameters": [
          {
            "name": "ticketId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/competitions/sessions/{sessionId}": {
      "get": {
        "tags": [
          "Competitions"
        ],
        "parameters": [
          {
            "name": "sessionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/competitions/sessions/{sessionId}/forfeit": {
      "post": {
        "tags": [
          "Competitions"
        ],
        "parameters": [
          {
            "name": "sessionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/matches": {
      "get": {
        "tags": [
          "Matches"
        ],
        "parameters": [
          {
            "name": "open",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MatchDto"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MatchDto"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/MatchDto"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Matches"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateMatchRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateMatchRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/CreateMatchRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/CreateMatchResponse"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateMatchResponse"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateMatchResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/matches/{matchId}": {
      "get": {
        "tags": [
          "Matches"
        ],
        "parameters": [
          {
            "name": "matchId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/MatchDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MatchDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/MatchDto"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Matches"
        ],
        "parameters": [
          {
            "name": "matchId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/matches/{matchId}/join": {
      "post": {
        "tags": [
          "Matches"
        ],
        "parameters": [
          {
            "name": "matchId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/MatchDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MatchDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/MatchDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/matches/join-by-code": {
      "post": {
        "tags": [
          "Matches"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JoinByCodeRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/JoinByCodeRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/JoinByCodeRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/MatchDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MatchDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/MatchDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/matchmaking/enqueue": {
      "post": {
        "tags": [
          "Matchmaking"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EnqueueMatchmakingRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/EnqueueMatchmakingRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/EnqueueMatchmakingRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/MatchmakingTicketDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MatchmakingTicketDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/MatchmakingTicketDto"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/matchmaking/tickets/{ticketId}": {
      "get": {
        "tags": [
          "Matchmaking"
        ],
        "parameters": [
          {
            "name": "ticketId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "$ref": "#/components/schemas/MatchmakingTicketDto"
                }
              },
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MatchmakingTicketDto"
                }
              },
              "text/json": {
                "schema": {
                  "$ref": "#/components/schemas/MatchmakingTicketDto"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Matchmaking"
        ],
        "parameters": [
          {
            "name": "ticketId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/me": {
      "get": {
        "tags": [
          "Profile"
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      },
      "put": {
        "tags": [
          "Profile"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateProfileRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateProfileRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateProfileRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/users/{userId}": {
      "get": {
        "tags": [
          "Profile"
        ],
        "parameters": [
          {
            "name": "userId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/replays/{replayId}": {
      "get": {
        "tags": [
          "Replays"
        ],
        "parameters": [
          {
            "name": "replayId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/replays/{replayId}/events": {
      "get": {
        "tags": [
          "Replays"
        ],
        "parameters": [
          {
            "name": "replayId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/replays/{replayId}/export": {
      "get": {
        "tags": [
          "Replays"
        ],
        "parameters": [
          {
            "name": "replayId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/rulesets/catalog": {
      "get": {
        "tags": [
          "Rulesets"
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/school/lesson": {
      "post": {
        "tags": [
          "School"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StartLessonRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/StartLessonRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/StartLessonRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/stats/me": {
      "get": {
        "tags": [
          "Stats"
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/leaderboard": {
      "get": {
        "tags": [
          "Stats"
        ],
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "schema": {
              "type": "integer",
              "format": "int32",
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "AuthResponse": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "string",
            "format": "uuid"
          },
          "username": {
            "type": "string"
          },
          "accessToken": {
            "type": "string"
          },
          "refreshToken": {
            "type": "string"
          }
        }
      },
      "CreateCompetitionRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "mode": {
            "type": "string"
          },
          "targetPoints": {
            "type": "integer",
            "format": "int32"
          },
          "ruleSets": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "CreateMatchRequest": {
        "type": "object",
        "properties": {
          "ruleSet": {
            "type": "string"
          },
          "isPrivate": {
            "type": "boolean"
          }
        }
      },
      "CreateMatchResponse": {
        "type": "object",
        "properties": {
          "matchId": {
            "type": "string",
            "format": "uuid"
          },
          "joinCode": {
            "type": "string"
          }
        }
      },
      "EnqueueMatchmakingRequest": {
        "type": "object",
        "properties": {
          "ruleSet": {
            "type": "string"
          }
        }
      },
      "JoinByCodeRequest": {
        "required": [
          "joinCode"
        ],
        "type": "object",
        "properties": {
          "joinCode": {
            "type": "string"
          }
        }
      },
      "JoinCompetitionInviteRequest": {
        "type": "object",
        "properties": {
          "joinCode": {
            "type": "string"
          }
        }
      },
      "LoginRequest": {
        "required": [
          "emailOrUsername",
          "password"
        ],
        "type": "object",
        "properties": {
          "emailOrUsername": {
            "type": "string"
          },
          "password": {
            "type": "string"
          }
        }
      },
      "MatchDto": {
        "type": "object",
        "properties": {
          "matchId": {
            "type": "string",
            "format": "uuid"
          },
          "ruleSet": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "whitePlayer": {
            "$ref": "#/components/schemas/UserDto"
          },
          "blackPlayer": {
            "$ref": "#/components/schemas/UserDto"
          },
          "createdUtc": {
            "type": "string",
            "format": "date-time"
          },
          "joinCode": {
            "type": "string",
            "nullable": true
          },
          "replayId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "winnerId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "winPoints": {
            "type": "integer",
            "format": "int32"
          },
          "competitionSessionId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "turnSeconds": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "MatchmakingTicketDto": {
        "type": "object",
        "properties": {
          "ticketId": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string"
          },
          "ruleSet": {
            "type": "string"
          },
          "matchId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "waitMs": {
            "type": "integer",
            "format": "int32"
          }
        }
      },
      "RefreshRequest": {
        "required": [
          "refreshToken"
        ],
        "type": "object",
        "properties": {
          "refreshToken": {
            "type": "string"
          }
        }
      },
      "RegisterRequest": {
        "required": [
          "email",
          "username",
          "password"
        ],
        "type": "object",
        "properties": {
          "email": {
            "type": "string"
          },
          "username": {
            "maxLength": 64,
            "minLength": 3,
            "type": "string"
          },
          "password": {
            "minLength": 6,
            "type": "string"
          }
        }
      },
      "SocialLoginRequest": {
        "required": [
          "provider"
        ],
        "type": "object",
        "properties": {
          "provider": {
            "maxLength": 32,
            "minLength": 3,
            "type": "string"
          },
          "idToken": {
            "type": "string",
            "nullable": true
          },
          "accessToken": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "StartLessonRequest": {
        "type": "object",
        "properties": {
          "ruleSet": {
            "type": "string"
          }
        }
      },
      "UpdateProfileRequest": {
        "type": "object",
        "properties": {
          "displayName": {
            "type": "string",
            "nullable": true
          },
          "avatarUrl": {
            "type": "string",
            "nullable": true
          },
          "turnTimerSeconds": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        }
      },
      "UserDto": {
        "type": "object",
        "properties": {
          "userId": {
            "type": "string",
            "format": "uuid"
          },
          "username": {
            "type": "string"
          },
          "avatarUrl": {
            "type": "string",
            "nullable": true
          },
          "rating": {
            "type": "integer",
            "format": "int32"
          }
        },
        "nullable": true
      }
    },
    "securitySchemes": {
      "Bearer": {
        "type": "http",
        "description": "JWT Authorization header using the Bearer scheme.",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      }
    }
  },
  "tags": [
    {
      "name": "TableHub.Api"
    },
    {
      "name": "Auth"
    },
    {
      "name": "Competitions"
    },
    {
      "name": "Matches"
    },
    {
      "name": "Matchmaking"
    },
    {
      "name": "Profile"
    },
    {
      "name": "Replays"
    },
    {
      "name": "Rulesets"
    },
    {
      "name": "School"
    },
    {
      "name": "Stats"
    }
  ]
}