{
  "openapi": "3.0.1",
  "info": {
    "title": "RpgRoller API",
    "version": "1.0.0"
  },
  "paths": {
    "/api/health": {
      "get": {
        "operationId": "getHealth",
        "responses": {
          "200": {
            "description": "API is reachable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HealthResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/rulesets": {
      "get": {
        "operationId": "getRulesets",
        "responses": {
          "200": {
            "description": "Supported rulesets.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/RulesetDefinition"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/auth/register": {
      "post": {
        "operationId": "registerUser",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Registered user.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserSummary"
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/auth/login": {
      "post": {
        "operationId": "loginUser",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Logged-in user.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/UserSummary"
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          }
        }
      }
    },
    "/api/auth/logout": {
      "post": {
        "operationId": "logoutUser",
        "responses": {
          "204": {
            "description": "Logged out."
          }
        }
      }
    },
    "/api/me": {
      "get": {
        "operationId": "getMe",
        "responses": {
          "200": {
            "description": "Current user context.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MeResponse"
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized."
          }
        }
      }
    },
    "/api/campaigns": {
      "get": {
        "operationId": "getCampaigns",
        "responses": {
          "200": {
            "description": "Accessible campaigns.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CampaignSummary"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized."
          }
        }
      },
      "post": {
        "operationId": "createCampaign",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCampaignRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created campaign.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignSummary"
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized."
          }
        }
      }
    },
    "/api/campaigns/{campaignId}": {
      "get": {
        "operationId": "getCampaign",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Campaign details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CampaignDetails"
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized."
          }
        }
      }
    },
    "/api/campaigns/{campaignId}/log": {
      "get": {
        "operationId": "getCampaignLog",
        "parameters": [
          {
            "name": "campaignId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Campaign log entries.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CampaignLogEntry"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized."
          }
        }
      }
    },
    "/api/characters": {
      "post": {
        "operationId": "createCharacter",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateCharacterRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created character.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CharacterSummary"
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized."
          }
        }
      }
    },
    "/api/characters/{characterId}": {
      "put": {
        "operationId": "updateCharacter",
        "parameters": [
          {
            "name": "characterId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateCharacterRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated character.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CharacterSummary"
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized."
          }
        }
      }
    },
    "/api/characters/{characterId}/activate": {
      "post": {
        "operationId": "activateCharacter",
        "parameters": [
          {
            "name": "characterId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Activation succeeded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "boolean"
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized."
          }
        }
      }
    },
    "/api/characters": {
      "get": {
        "operationId": "getOwnCharacters",
        "responses": {
          "200": {
            "description": "Characters owned by the user.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CharacterSummary"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized."
          }
        }
      }
    },
    "/api/characters/{characterId}/skills": {
      "post": {
        "operationId": "createSkill",
        "parameters": [
          {
            "name": "characterId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSkillRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created skill.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SkillSummary"
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized."
          }
        }
      }
    },
    "/api/skills/{skillId}": {
      "put": {
        "operationId": "updateSkill",
        "parameters": [
          {
            "name": "skillId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateSkillRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated skill.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SkillSummary"
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized."
          }
        }
      }
    },
    "/api/skills/{skillId}/roll": {
      "post": {
        "operationId": "rollSkill",
        "parameters": [
          {
            "name": "skillId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RollSkillRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Roll result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RollResult"
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiError"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized."
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "HealthResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string"
          }
        },
        "required": [
          "status"
        ]
      },
      "ApiError": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ]
      },
      "RegisterRequest": {
        "type": "object",
        "properties": {
          "username": {
            "type": "string"
          },
          "password": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          }
        },
        "required": [
          "username",
          "password",
          "displayName"
        ]
      },
      "LoginRequest": {
        "type": "object",
        "properties": {
          "username": {
            "type": "string"
          },
          "password": {
            "type": "string"
          }
        },
        "required": [
          "username",
          "password"
        ]
      },
      "UserSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "username": {
            "type": "string"
          },
          "displayName": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "username",
          "displayName"
        ]
      },
      "MeResponse": {
        "type": "object",
        "properties": {
          "user": {
            "$ref": "#/components/schemas/UserSummary"
          },
          "activeCharacterId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "currentCampaignId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        },
        "required": [
          "user"
        ]
      },
      "RulesetDefinition": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "diceSyntax": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "name",
          "diceSyntax"
        ]
      },
      "CreateCampaignRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "rulesetId": {
            "type": "string"
          }
        },
        "required": [
          "name",
          "rulesetId"
        ]
      },
      "CampaignSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "rulesetId": {
            "type": "string"
          },
          "gmUserId": {
            "type": "string",
            "format": "uuid"
          }
        },
        "required": [
          "id",
          "name",
          "rulesetId",
          "gmUserId"
        ]
      },
      "CampaignDetails": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "rulesetId": {
            "type": "string"
          },
          "gm": {
            "$ref": "#/components/schemas/UserSummary"
          },
          "characters": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CharacterSummary"
            }
          },
          "skills": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SkillSummary"
            }
          }
        },
        "required": [
          "id",
          "name",
          "rulesetId",
          "gm",
          "characters",
          "skills"
        ]
      },
      "CreateCharacterRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "campaignId": {
            "type": "string",
            "format": "uuid"
          }
        },
        "required": [
          "name",
          "campaignId"
        ]
      },
      "UpdateCharacterRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "campaignId": {
            "type": "string",
            "format": "uuid"
          }
        },
        "required": [
          "name",
          "campaignId"
        ]
      },
      "CharacterSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "ownerUserId": {
            "type": "string",
            "format": "uuid"
          },
          "campaignId": {
            "type": "string",
            "format": "uuid"
          }
        },
        "required": [
          "id",
          "name",
          "ownerUserId",
          "campaignId"
        ]
      },
      "CreateSkillRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "diceRollDefinition": {
            "type": "string"
          },
          "wildDice": {
            "type": "integer",
            "format": "int32"
          },
          "allowFumble": {
            "type": "boolean"
          },
          "skillGroupId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "fumbleRange": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        },
        "required": [
          "name",
          "diceRollDefinition",
          "wildDice",
          "allowFumble"
        ]
      },
      "UpdateSkillRequest": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "diceRollDefinition": {
            "type": "string"
          },
          "wildDice": {
            "type": "integer",
            "format": "int32"
          },
          "allowFumble": {
            "type": "boolean"
          },
          "skillGroupId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "fumbleRange": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        },
        "required": [
          "name",
          "diceRollDefinition",
          "wildDice",
          "allowFumble"
        ]
      },
      "SkillSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "characterId": {
            "type": "string",
            "format": "uuid"
          },
          "skillGroupId": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "name": {
            "type": "string"
          },
          "diceRollDefinition": {
            "type": "string"
          },
          "wildDice": {
            "type": "integer",
            "format": "int32"
          },
          "allowFumble": {
            "type": "boolean"
          },
          "fumbleRange": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        },
        "required": [
          "id",
          "characterId",
          "name",
          "diceRollDefinition",
          "wildDice",
          "allowFumble"
        ]
      },
      "RollSkillRequest": {
        "type": "object",
        "properties": {
          "visibility": {
            "type": "string"
          }
        },
        "required": [
          "visibility"
        ]
      },
      "RollDieResult": {
        "type": "object",
        "properties": {
          "roll": {
            "type": "integer",
            "format": "int32"
          },
          "crit": {
            "type": "boolean"
          },
          "fumble": {
            "type": "boolean"
          },
          "wild": {
            "type": "boolean"
          },
          "removed": {
            "type": "boolean"
          },
          "added": {
            "type": "boolean"
          },
          "sequence": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          },
          "kind": {
            "type": "string",
            "nullable": true
          },
          "signedContribution": {
            "type": "integer",
            "format": "int32",
            "nullable": true
          }
        },
        "required": [
          "roll",
          "crit",
          "fumble",
          "wild",
          "removed",
          "added"
        ]
      },
      "RollResult": {
        "type": "object",
        "properties": {
          "rollId": {
            "type": "string",
            "format": "uuid"
          },
          "campaignId": {
            "type": "string",
            "format": "uuid"
          },
          "characterId": {
            "type": "string",
            "format": "uuid"
          },
          "skillId": {
            "type": "string",
            "format": "uuid"
          },
          "rollerUserId": {
            "type": "string",
            "format": "uuid"
          },
          "visibility": {
            "type": "string"
          },
          "result": {
            "type": "integer",
            "format": "int32"
          },
          "breakdown": {
            "type": "string"
          },
          "dice": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RollDieResult"
            }
          },
          "timestampUtc": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "rollId",
          "campaignId",
          "characterId",
          "skillId",
          "rollerUserId",
          "visibility",
          "result",
          "breakdown",
          "dice",
          "timestampUtc"
        ]
      },
      "CampaignLogEntry": {
        "type": "object",
        "properties": {
          "rollId": {
            "type": "string",
            "format": "uuid"
          },
          "campaignId": {
            "type": "string",
            "format": "uuid"
          },
          "characterId": {
            "type": "string",
            "format": "uuid"
          },
          "skillId": {
            "type": "string",
            "format": "uuid"
          },
          "rollerUserId": {
            "type": "string",
            "format": "uuid"
          },
          "visibility": {
            "type": "string"
          },
          "result": {
            "type": "integer",
            "format": "int32"
          },
          "breakdown": {
            "type": "string"
          },
          "timestampUtc": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "rollId",
          "campaignId",
          "characterId",
          "skillId",
          "rollerUserId",
          "visibility",
          "result",
          "breakdown",
          "timestampUtc"
        ]
      }
    }
  }
}
