openapi: 3.0.3

info:
  title: Adventurers Guild API
  version: 1.19.0
  description: |
    Fantasy-themed API designed for backend testing, API automation, and contract validation practice.

    The documentation in this file reflects the current public routes implemented by the API:

    - Auth token issuance
    - Attributes list
    - Backgrounds list and detail
    - Equipment list and detail
    - Skills list and detail
    - Classes list and detail
    - Spells list and detail
    - Species list and detail
    - Character management, deletion, equipment flows, ability score flows, derived stats summary, character skill flows, and character spell flows

servers:
  - url: http://localhost:3000
    description: Local development
  - url: https://adventurers-guild-api.vercel.app
    description: Production

tags:
  - name: Auth
    description: Authentication and bearer token issuance.
  - name: Attributes
    description: Core RPG attributes such as Strength, Dexterity, and Wisdom.
  - name: Backgrounds
    description: Character backgrounds list and detail endpoints.
  - name: Equipment
    description: Equipment list and detail endpoints for weapons, armor, shields, and generic gear.
  - name: Skills
    description: Skills list and skill detail endpoints.
  - name: Classes
    description: Playable RPG classes, including detail payloads.
  - name: Spells
    description: Spell list and spell detail endpoints.
  - name: Species
    description: Species list and species detail endpoints.
  - name: Characters
    description: Protected character management, deletion, equipment tracking, derived stats summary, and derived ability, skill, and spell endpoints.

x-tagGroups:
  - name: Access
    tags:
      - Auth
  - name: Core Resources
    tags:
      - Attributes
      - Backgrounds
      - Equipment
      - Skills
      - Classes
      - Spells
      - Species
  - name: Character Management
    tags:
      - Characters

paths:
  /api/auth/token:
    post:
      tags:
        - Auth
      operationId: issueAuthToken
      summary: Issue bearer token
      description: |
        Validates owner credentials and returns a JWT-like bearer token.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenRequestBody'
            example:
              username: <your-username>
              password: <your-password>
      responses:
        '200':
          description: Token issued successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponseBody'
              example:
                token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwib3duZXJJZCI6MSwidXNlcm5hbWUiOiJkZW1vIiwiaWF0IjoxNzAwMDAwMDAwLCJleHAiOjE3MDAwMDM2MDB9.signature
        '400':
          description: Invalid token request payload
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Invalid token request payload
        '401':
          description: Invalid credentials
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Invalid credentials
        '500':
          description: Failed to issue token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to issue token

  /api/attributes:
    get:
      tags:
        - Attributes
      operationId: getAttributes
      summary: List all attributes
      description: |
        Returns the six core RPG attributes with their descriptions and the skill names associated with each attribute.
      responses:
        '200':
          description: Attributes returned successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Attribute'
              example:
                - id: 1
                  name: Strength
                  shortname: STR
                  description: Measures physical power, carrying capacity, and effectiveness in brute-force actions such as lifting, pushing, and melee attacks.
                  skills:
                    - Athletics
                - id: 2
                  name: Dexterity
                  shortname: DEX
                  description: Measures agility, reflexes, balance, and coordination. It affects actions that require speed, precision, and stealth.
                  skills:
                    - Acrobatics
                    - Sleight of Hand
                    - Stealth

  /api/backgrounds:
    get:
      tags:
        - Backgrounds
      operationId: getBackgrounds
      summary: List all backgrounds
      description: |
        Returns a lightweight list of character backgrounds.

        This endpoint exposes only:
        - `id`
        - `name`
      responses:
        '200':
          description: Backgrounds returned successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/BackgroundListItem'
              example:
                - id: 1
                  name: Acolyte
                - id: 5
                  name: Criminal
                - id: 13
                  name: Sage
                - id: 16
                  name: Soldier
        '500':
          description: Failed to fetch backgrounds
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to fetch backgrounds

  /api/backgrounds/{identifier}:
    get:
      tags:
        - Backgrounds
      operationId: getBackgroundByIdentifier
      summary: Get background detail by identifier
      description: |
        Returns detailed information about a background.

        Supported identifiers:
        - numeric id, for example `/api/backgrounds/1`
        - slug, for example `/api/backgrounds/acolyte`
        - name or slug in a case-insensitive form, for example `/api/backgrounds/SOLDIER`
      parameters:
        - name: identifier
          in: path
          required: true
          description: Background identifier as numeric id, name, or slug. Name and slug lookup are case-insensitive.
          schema:
            type: string
          examples:
            byId:
              summary: Search by id
              value: '1'
            bySlug:
              summary: Search by slug
              value: acolyte
            byUppercaseName:
              summary: Search by uppercase name
              value: SOLDIER
      responses:
        '200':
          description: Background found successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BackgroundDetail'
              examples:
                acolyte:
                  summary: Acolyte detail
                  value:
                    id: 1
                    name: Acolyte
                    slug: acolyte
                    description: You devoted yourself to service in a temple, either nestled in a town or secluded in a sacred grove. There you performed rites in honor of a god or pantheon. You served under a priest and studied religion. Thanks to your priest's instruction and your own devotion, you also learned how to channel a modicum of divine power in service to your place of worship and the people who prayed there.
                    abilityScores:
                      - INT
                      - WIS
                      - CHA
                    feat: Magic Initiate (Cleric)
                    skillProficiencies:
                      - Insight
                      - Religion
                    toolProficiency: Calligrapher's Supplies
                    equipmentOptions:
                      - Calligrapher's Supplies, Book (prayers), Holy Symbol, Parchment (10 sheets), Robe, 8 GP
                      - 50 GP
                soldier:
                  summary: Soldier detail
                  value:
                    id: 16
                    name: Soldier
                    slug: soldier
                    description: You began training for war as soon as you reached adulthood and carry precious few memories of life before you took up arms. Battle is in your blood. Sometimes you catch yourself reflexively performing the basic fighting exercises you learned first. Eventually, you put that training to use on the battlefield, protecting the realm by waging war.
                    abilityScores:
                      - STR
                      - DEX
                      - CON
                    feat: Savage Attacker
                    skillProficiencies:
                      - Athletics
                      - Intimidation
                    toolProficiency: One kind of Gaming Set
                    equipmentOptions:
                      - Spear, Shortbow, 20 Arrows, Gaming Set (same as above), Healer's Kit, Quiver, Traveler's Clothes, 14 GP
                      - 50 GP
        '404':
          description: Background not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Background not found
        '500':
          description: Failed to fetch background detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to fetch background detail

  /api/equipment:
    get:
      tags:
        - Equipment
      operationId: getEquipment
      summary: List all equipment
      description: |
        Returns a lightweight list of equipment items.
      responses:
        '200':
          description: Equipment returned successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/EquipmentListItem'
              example:
                - id: 1
                  name: Club
                  category: Weapon
                  type: Weapon
                  cost: 1 sp
                  weight: 2
                  isMagical: false
                - id: 58
                  name: Chain Mail
                  category: Armor
                  type: Armor
                  cost: 75 gp
                  weight: 55
                  isMagical: false
        '500':
          description: Failed to fetch equipment
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to fetch equipment

  /api/equipment/{identifier}:
    get:
      tags:
        - Equipment
      operationId: getEquipmentByIdentifier
      summary: Get equipment detail by identifier
      description: |
        Returns detailed information about a single equipment item.

        Supported identifiers:
        - numeric id, for example `/api/equipment/1`
        - slug, for example `/api/equipment/chain-mail`
        - exact name in case-insensitive form, for example `/api/equipment/shield`
      parameters:
        - name: identifier
          in: path
          required: true
          description: Equipment id, slug, or exact name.
          schema:
            oneOf:
              - type: integer
                minimum: 1
              - type: string
          examples:
            byId:
              summary: Lookup by id
              value: 1
            bySlug:
              summary: Lookup by slug
              value: chain-mail
            byName:
              summary: Lookup by name
              value: Shield
      responses:
        '200':
          description: Equipment found successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EquipmentDetail'
              example:
                id: 1
                name: Club
                slug: club
                category: Weapon
                type: Weapon
                description: A simple melee weapon.
                cost: 1 sp
                weight: 2
                isMagical: false
                modifiers: []
                effects: []
                details:
                  kind: weapon
                  weaponCategory: Simple
                  attackType: Melee
                  damage:
                    formula: 1d4
                    dice:
                      - count: 1
                        value: 4
                    bonus: 0
                    damageType: Bludgeoning
                  versatileDamage: null
                  properties:
                    - name: Light
                      slug: light
                  mastery:
                    name: Slow
                    slug: slow
                  range: null
                  proficiencyType: Simple Weapons
                  ammunitionType: null
        '404':
          description: Equipment not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Equipment not found
        '500':
          description: Failed to fetch equipment detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to fetch equipment detail

  /api/skills:
    get:
      tags:
        - Skills
      operationId: getSkills
      summary: List all skills
      description: |
        Returns a lightweight list of available skills.

        This endpoint exposes only:
        - `id`
        - `name`
      responses:
        '200':
          description: Skills returned successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/SkillListItem'
              example:
                - id: 1
                  name: Athletics
                - id: 2
                  name: Acrobatics
                - id: 3
                  name: Sleight of Hand
                - id: 4
                  name: Stealth

  /api/skills/{identifier}:
    get:
      tags:
        - Skills
      operationId: getSkillByIdentifier
      summary: Get skill detail by identifier
      description: |
        Returns detailed information about a skill.

        Supported identifiers:
        - numeric id, for example `/api/skills/1`
        - slug, for example `/api/skills/animal-handling`
        - normalized name, for example `/api/skills/stealth`
      parameters:
        - name: identifier
          in: path
          required: true
          description: Skill identifier as numeric id, slug, or normalized name.
          schema:
            type: string
          examples:
            byId:
              summary: Search by id
              value: '1'
            bySlug:
              summary: Search by slug
              value: animal-handling
            byName:
              summary: Search by normalized name
              value: stealth
      responses:
        '200':
          description: Skill found successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SkillDetail'
              examples:
                athletics:
                  summary: Athletics detail
                  value:
                    id: 1
                    name: Athletics
                    attribute: STR
                    description: Represents physical capability in actions such as climbing, jumping, swimming, or forcing objects through strength.
                    exampleofuse: Used when a character tries to climb a castle wall or force open a heavy gate.
                    commonclasses:
                      - Fighter
                      - Barbarian
                      - Paladin
                      - Ranger
                stealth:
                  summary: Stealth detail
                  value:
                    id: 4
                    name: Stealth
                    attribute: DEX
                    description: Represents the ability to move silently and remain unseen.
                    exampleofuse: Used when sneaking past enemies or hiding.
                    commonclasses:
                      - Rogue
                      - Ranger
                      - Monk
                      - Bard
        '404':
          description: Skill not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Skill not found

  /api/classes:
    get:
      tags:
        - Classes
      operationId: getClasses
      summary: List all classes
      description: |
        Returns a lightweight list of RPG classes.

        This endpoint exposes only:
        - `id`
        - `name`
      responses:
        '200':
          description: Classes returned successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/ClassListItem'
              example:
                - id: 1
                  name: Barbarian
                - id: 2
                  name: Bard
                - id: 3
                  name: Cleric
                - id: 4
                  name: Druid
        '500':
          description: Failed to fetch classes
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to fetch classes

  /api/classes/{identifier}:
    get:
      tags:
        - Classes
      operationId: getClassByIdentifier
      summary: Get class detail by identifier
      description: |
        Returns detailed information about a class.

        Supported identifiers:
        - numeric id, for example `/api/classes/12`
        - slug, for example `/api/classes/wizard`
        - normalized name, for example `/api/classes/fighter`
      parameters:
        - name: identifier
          in: path
          required: true
          description: Class identifier as numeric id, slug, or normalized name.
          schema:
            type: string
          examples:
            byId:
              summary: Search by id
              value: '12'
            bySlug:
              summary: Search by slug
              value: wizard
            byName:
              summary: Search by normalized name
              value: fighter
      responses:
        '200':
          description: Class found successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ClassDetail'
              examples:
                barbarian:
                  summary: Barbarian detail
                  value:
                    id: 1
                    name: Barbarian
                    slug: barbarian
                    description: A fierce warrior who relies on raw strength and primal fury to overcome enemies.
                    role: melee
                    hitdie: 12
                    primaryattributes:
                      - STR
                    recommendedskills:
                      - Athletics
                      - Survival
                      - Intimidation
                    savingthrows:
                      - STR
                      - CON
                    spellcasting: null
                    skillProficiencyChoices:
                      choose: 2
                      options:
                        - Animal Handling
                        - Athletics
                        - Intimidation
                        - Nature
                        - Perception
                        - Survival
                    weaponProficiencies:
                      - Simple Weapons
                      - Martial Weapons
                    armorTraining:
                      - Light Armor
                      - Medium Armor
                      - Shield
                    startingEquipmentOptions:
                      - label: A
                        items:
                          - Greataxe
                          - 4 Handaxes
                          - Explorer's Pack
                          - 15 GP
                      - label: B
                        items:
                          - 75 GP
                    equipmentOptions:
                      - Greataxe, 4 Handaxes, Explorer's Pack, and 15 GP
                      - 75 GP
                    subclasses:
                      - Berserker
                      - Wild Heart
                      - World Tree
                      - Zealot
                    levelprogression: []
                wizard:
                  summary: Wizard detail
                  value:
                    id: 12
                    name: Wizard
                    slug: wizard
                    description: A learned arcane scholar who studies the inner workings of magic to prepare spells, master rituals, and wield unmatched magical versatility.
                    role: caster
                    hitdie: 6
                    primaryattributes:
                      - INT
                    recommendedskills:
                      - Arcana
                      - Investigation
                      - History
                      - Nature
                      - Religion
                    savingthrows:
                      - INT
                      - WIS
                    spellcasting:
                      ability: INT
                      usesSpellbook: true
                      canCastRituals: true
                      selection:
                        mode: spellbook_plus_prepared
                        selectionType: prepared
                        changesWhen: long_rest
                        cantrips:
                          '1': 3
                        preparedSpells:
                          '1': 4
                      spellbookSpells:
                        '1': 6
                      spellsAddedPerLevel: 2
                    skillProficiencyChoices:
                      choose: 2
                      options:
                        - Arcana
                        - History
                        - Insight
                        - Investigation
                        - Medicine
                        - Religion
                    weaponProficiencies:
                      - Simple Weapons
                    armorTraining: []
                    startingEquipmentOptions:
                      - label: A
                        items:
                          - Quarterstaff
                          - Scholar's Pack
                          - Spellbook
                          - 5 GP
                      - label: B
                        items:
                          - Dagger
                          - Scholar's Pack
                          - Spellbook
                          - 5 GP
                    equipmentOptions:
                      - Quarterstaff, Scholar's Pack, Spellbook, and 5 GP
                      - Dagger, Scholar's Pack, Spellbook, and 5 GP
                    subclasses:
                      - Evoker
                    levelprogression: []
        '404':
          description: Class not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Class not found
        '500':
          description: Failed to fetch class detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to fetch class detail

  /api/spells:
    get:
      tags:
        - Spells
      operationId: getSpells
      summary: List all spells
      description: |
        Returns a lightweight list of spells.

        This endpoint exposes:
        - `id`
        - `name`
        - `level`
        - `levelLabel`
      parameters:
        - name: level
          in: query
          required: false
          description: Spell level filter. Accepts a non-negative integer or `cantrip` for level 0.
          schema:
            type: string
          examples:
            levelOne:
              value: '1'
            cantrip:
              value: cantrip
        - name: school
          in: query
          required: false
          description: Case-insensitive spell school filter.
          schema:
            type: string
          example: evocation
        - name: class
          in: query
          required: false
          description: Case-insensitive class filter by class slug or name.
          schema:
            type: string
          example: wizard
        - name: source
          in: query
          required: false
          description: Case-insensitive spell source filter.
          schema:
            type: string
          example: players-handbook
        - name: name
          in: query
          required: false
          description: Case-insensitive partial name match.
          schema:
            type: string
          example: acid
      responses:
        '200':
          description: Spells returned successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/SpellListItem'
              example:
                - id: 1
                  name: Acid Splash
                  level: 0
                  levelLabel: Cantrip
                - id: 18
                  name: Alarm
                  level: 1
                  levelLabel: Level 1
                - id: 20
                  name: Animal Friendship
                  level: 1
                  levelLabel: Level 1
        '400':
          description: Invalid spell filter
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Invalid level filter
        '500':
          description: Failed to fetch spells
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to fetch spells

  /api/spells/{identifier}:
    get:
      tags:
        - Spells
      operationId: getSpellByIdentifier
      summary: Get spell detail by identifier
      description: |
        Returns detailed information about a spell.

        Supported identifiers:
        - numeric id, for example `/api/spells/18`
        - slug, for example `/api/spells/acid-splash`
        - normalized name, for example `/api/spells/alarm`
      parameters:
        - name: identifier
          in: path
          required: true
          description: Spell identifier as numeric id, slug, or normalized name.
          schema:
            type: string
          examples:
            byId:
              summary: Search by id
              value: '18'
            bySlug:
              summary: Search by slug
              value: acid-splash
            byName:
              summary: Search by normalized name
              value: alarm
      responses:
        '200':
          description: Spell found successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SpellDetail'
              examples:
                acidSplash:
                  summary: Acid Splash detail
                  value:
                    id: 1
                    name: Acid Splash
                    slug: acid-splash
                    source: Player's Handbook
                    school: Evocation
                    level: 0
                    levelLabel: Cantrip
                    castingTime: Action
                    range: 60 feet
                    components:
                      verbal: true
                      somatic: true
                      material: false
                      materialDescription: null
                    duration: Instantaneous
                    description: You create an acidic bubble at a point within range, where it explodes in a 5-foot-radius Sphere. Each creature in that Sphere must succeed on a Dexterity saving throw or take 1d6 Acid damage.
                    classes:
                      - Sorcerer
                      - Wizard
                    scaling:
                      entries:
                        - level: 5
                          description: damage increases to 2d6
                        - level: 11
                          description: damage increases to 3d6
                        - level: 17
                          description: damage increases to 4d6
                alarm:
                  summary: Alarm detail
                  value:
                    id: 18
                    name: Alarm
                    slug: alarm
                    source: Player's Handbook
                    school: Abjuration
                    level: 1
                    levelLabel: Level 1
                    castingTime: 1 minute or Ritual
                    range: 30 feet
                    components:
                      verbal: true
                      somatic: true
                      material: true
                      materialDescription: a bell and silver wire
                    duration: 8 hours
                    description: |
                      You set an alarm against intrusion. Choose a door, a window, or an area within range that is no larger than a 20-foot Cube. Until the spell ends, an alarm alerts you whenever a creature touches or enters the warded area. When you cast the spell, you can designate creatures that won't set off the alarm. You also choose whether the alarm is audible or mental:

                      Audible Alarm. The alarm produces the sound of a handbell for 10 seconds within 60 feet of the warded area.

                      Mental Alarm. You are alerted by a mental ping if you are within 1 mile of the warded area. This ping awakens you if you're asleep.
                    classes:
                      - Ranger
                      - Wizard
                    scaling: null
        '404':
          description: Spell not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Spell not found
        '500':
          description: Failed to fetch spell detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to fetch spell detail

  /api/species:
    get:
      tags:
        - Species
      operationId: getSpecies
      summary: List all species
      description: |
        Returns a lightweight list of species.

        This endpoint exposes only:
        - `id`
        - `name`
      responses:
        '200':
          description: Species returned successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/SpeciesListItem'
              example:
                - id: 1
                  name: Dragonborn
                - id: 2
                  name: Dwarf
                - id: 3
                  name: Elf
        '500':
          description: Failed to fetch species
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to fetch species

  /api/species/{identifier}:
    get:
      tags:
        - Species
      operationId: getSpeciesByIdentifier
      summary: Get species detail by identifier
      description: |
        Returns detailed information about a species.

        Supported identifiers:
        - numeric id, for example `/api/species/1`
        - slug, for example `/api/species/dragonborn`
        - normalized name, for example `/api/species/elf`
      parameters:
        - name: identifier
          in: path
          required: true
          description: Species identifier as numeric id, name, or slug. Name and slug lookup are case-insensitive.
          schema:
            type: string
          examples:
            byId:
              summary: Search by id
              value: '1'
            bySlug:
              summary: Search by slug
              value: dragonborn
            byName:
              summary: Search by name
              value: elf
      responses:
        '200':
          description: Species found successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SpeciesDetail'
              examples:
                dragonborn:
                  summary: Dragonborn detail
                  value:
                    id: 1
                    name: Dragonborn
                    slug: dragonborn
                    description: Dragonborn look like wingless, bipedal dragons.
                    creatureType: Humanoid
                    size: Medium
                    speed: 30
                    specialTraits:
                      - name: Draconic Ancestry
                        description: You have a dragon ancestor, which grants you a Breath Weapon and damage resistance tied to that ancestry.
                    subspecies:
                      - name: Black Dragon Ancestry
                        slug: black-dragon-ancestry
                        description: A dragonborn ancestry tied to black dragons and acid damage.
                        specialTraits:
                          - name: Damage Type
                            description: Your Draconic Ancestry damage type is Acid.
                elf:
                  summary: Elf detail
                  value:
                    id: 3
                    name: Elf
                    slug: elf
                    description: Elves are a magical people of otherworldly grace.
                    creatureType: Humanoid
                    size: Medium
                    speed: 30
                    specialTraits:
                      - name: Darkvision
                        description: You have Darkvision with a range of 60 feet.
                    subspecies:
                      - name: High Elf
                        slug: high-elf
                        description: A magical elven lineage with an arcane cantrip and additional arcane magic.
                        specialTraits:
                          - name: Level 1
                            description: You know the Prestidigitation cantrip.
        '404':
          description: Species not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Species not found
        '500':
          description: Failed to fetch species detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to fetch species detail

  /api/characters:
    get:
      tags:
        - Characters
      operationId: getCharacters
      summary: List authenticated owner characters
      description: |
        Returns the characters owned by the authenticated owner.
      security:
        - bearerAuth: []
      responses:
        '200':
          description: Characters returned successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/CharacterListItem'
              example:
                - id: 101
                  name: Merien
                  status: draft
                  level: 1
                - id: 102
                  name: Arin
                  status: complete
                  level: 3
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Unauthorized
        '500':
          description: Failed to fetch characters
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to fetch characters
    post:
      tags:
        - Characters
      operationId: createCharacter
      summary: Create character
      description: |
        Creates a character for the authenticated owner.
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CharacterCreateRequestBody'
            examples:
              draftCharacter:
                summary: Create draft character
                value:
                  name: Merien
              partialCharacter:
                summary: Create character with class
                value:
                  name: Arin
                  classId: 12
              withAbilityScores:
                summary: Create character with initial ability scores
                value:
                  name: Merien
                  classId: 12
                  speciesId: 3
                  backgroundId: 13
                  skillProficiencies:
                    - Arcana
                    - History
                  abilityScores:
                    base:
                      STR: 8
                      DEX: 14
                      CON: 13
                      INT: 15
                      WIS: 12
                      CHA: 10
                    bonuses:
                      STR: 0
                      DEX: 0
                      CON: 0
                      INT: 2
                      WIS: 1
                      CHA: 0
      responses:
        '201':
          description: Character created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CharacterResponseBody'
              example:
                id: 101
                name: Merien
                status: draft
                classId: null
                speciesId: null
                backgroundId: null
                level: 1
                missingFields:
                  - classId
                  - speciesId
                  - backgroundId
                pendingChoices: []
                abilityScores: null
                abilityModifiers: null
                armorClass:
                  total: 10
                  base: 10
                  dexModifierApplied: 0
                  classBonus: 0
                  shieldBonus: 0
                  sources:
                    - name: Base AC
                      type: base
                      value: 10
                weaponAttacks: []
                hitPoints: null
                savingThrows: []
                initiative: null
                passivePerception: null
                movement: null
                inventoryWeight:
                  total: 0
                  unit: lb
                  sources: []
                spellcastingSummary:
                  canCastSpells: false
                  ability: null
                  abilityModifier: null
                  spellSaveDc: null
                  spellAttackBonus: null
                  selectedSpellsCount: 0
                  selectedCantripsCount: 0
                spellSlots: []
                selectedSpells: []
                currency: null
                skillProficiencies: []
                abilityScoreRules: null
                classDetails: null
                speciesDetails: null
                backgroundDetails: null
        '400':
          description: Invalid character request payload
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Invalid character request payload
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Unauthorized
        '500':
          description: Failed to create character
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to create character

  /api/characters/{id}:
    get:
      tags:
        - Characters
      operationId: getCharacterById
      summary: Get character detail
      description: |
        Returns a single character owned by the authenticated owner.

        When related data exists, the detail payload can include nested:
        - `abilityScores`
        - `abilityModifiers`
        - `armorClass`
        - `weaponAttacks`
        - `hitPoints`
        - `savingThrows`
        - `pendingChoices`
        - `initiative`
        - `passivePerception`
        - `movement`
        - `inventoryWeight`
        - `spellcastingSummary`
        - `spellSlots`
        - `selectedSpells`
        - `currency`
        - `skillProficiencies`
        - `abilityScoreRules`
        - `classDetails`
        - `speciesDetails`
        - `backgroundDetails`

        `armorClass` is calculated from resolved DEX modifier and currently equipped armor or shield. If no armor is equipped, the base AC is `10`; Barbarian and Monk unarmored defense can contribute a `class` source when their rules apply.

        `weaponAttacks` is derived from currently equipped weapons, class weapon proficiencies, character level, and resolved ability modifiers. If no weapon is equipped, it is returned as an empty array.

        `hitPoints` is derived from the character's class hit die, level, and resolved CON modifier. It is returned as `null` until class details and ability modifiers are available; when calculated, `current` starts equal to `max` and `temporary` starts at `0`.

        `savingThrows` is derived from class saving throw proficiencies, character level, and resolved ability modifiers. It is returned as an empty array until class details and ability modifiers are available; when calculated, it is ordered as `STR`, `DEX`, `CON`, `INT`, `WIS`, `CHA`.

        `initiative` is derived from the resolved DEX modifier plus the current static bonus (`0`). It is returned as `null` until ability modifiers are available.

        `passivePerception` is derived from the calculated Perception skill total. Its current formula is `10 + skillModifier + bonus`, with `bonus` currently `0`. It is returned as `null` until ability modifiers are available.

        `movement` is derived from `speciesDetails.speed` and currently uses `ft` as the unit. It is returned as `null` until species details with a numeric speed are available.

        `inventoryWeight` is derived from character equipment rows with a non-null equipment weight. Each source uses `equipment.weight * quantity`; when the character has no weighted equipment, it returns `{ "total": 0, "unit": "lb", "sources": [] }`.

        `pendingChoices` lists unresolved package-selection steps that still need to be completed through the dedicated equipment choice endpoints. It is currently used for `classEquipmentSelection` and `backgroundEquipmentSelection`.

        `spellcastingSummary` is derived from the character class spellcasting metadata, character level, resolved spellcasting ability modifier, and selected spells. For non-casters, `canCastSpells` is `false`, spellcasting ability values are `null`, and selected spell counts are `0`.

        `spellSlots` is derived from class spellcasting progression and character level. It is returned as an empty array for non-casters or classes without supported slot progression; `used` currently starts at `0`, and `available` is `max - used`.

        `selectedSpells` contains enriched spell details for the character's selected spells, ordered by spell level and spell id. It is returned as an empty array when no spells are selected.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/CharacterId'
      responses:
        '200':
          description: Character found successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CharacterResponseBody'
              example:
                id: 101
                name: Merien
                status: complete
                classId: 12
                speciesId: 3
                backgroundId: 13
                level: 1
                missingFields: []
                pendingChoices: []
                skillProficiencies:
                  - Arcana
                  - History
                abilityScores:
                  base:
                    STR: 8
                    DEX: 14
                    CON: 13
                    INT: 15
                    WIS: 12
                    CHA: 10
                  bonuses:
                    STR: 0
                    DEX: 0
                    CON: 0
                    INT: 2
                    WIS: 1
                    CHA: 0
                  final:
                    STR: 8
                    DEX: 14
                    CON: 13
                    INT: 17
                    WIS: 13
                    CHA: 10
                abilityModifiers:
                  STR: -1
                  DEX: 2
                  CON: 1
                  INT: 3
                  WIS: 1
                  CHA: 0
                armorClass:
                  total: 12
                  base: 10
                  dexModifierApplied: 2
                  classBonus: 0
                  shieldBonus: 0
                  sources:
                    - name: Base AC
                      type: base
                      value: 10
                weaponAttacks:
                  - equipmentId: 42
                    name: Shortbow
                    attackType: ranged
                    ability: DEX
                    isProficient: true
                    abilityModifier: 2
                    proficiencyBonus: 2
                    attackBonus: 4
                    damage:
                      formula: 1d6 + 2
                      base: 1d6
                      modifier: 2
                      damageType: Piercing
                    properties:
                      - Ammunition
                      - Two-Handed
                    range:
                      normal: 80
                      long: 320
                      unit: ft
                hitPoints:
                  max: 7
                  current: 7
                  temporary: 0
                  hitDie: 6
                  conModifier: 1
                  calculation: 6 + 1
                savingThrows:
                  - ability: STR
                    isProficient: false
                    abilityModifier: -1
                    proficiencyBonus: 0
                    bonus: 0
                    total: -1
                    sources:
                      - type: abilityModifier
                        value: -1
                  - ability: DEX
                    isProficient: false
                    abilityModifier: 2
                    proficiencyBonus: 0
                    bonus: 0
                    total: 2
                    sources:
                      - type: abilityModifier
                        value: 2
                  - ability: CON
                    isProficient: false
                    abilityModifier: 1
                    proficiencyBonus: 0
                    bonus: 0
                    total: 1
                    sources:
                      - type: abilityModifier
                        value: 1
                  - ability: INT
                    isProficient: true
                    abilityModifier: 3
                    proficiencyBonus: 2
                    bonus: 0
                    total: 5
                    sources:
                      - type: abilityModifier
                        value: 3
                      - type: classProficiency
                        value: 2
                  - ability: WIS
                    isProficient: true
                    abilityModifier: 1
                    proficiencyBonus: 2
                    bonus: 0
                    total: 3
                    sources:
                      - type: abilityModifier
                        value: 1
                      - type: classProficiency
                        value: 2
                  - ability: CHA
                    isProficient: false
                    abilityModifier: 0
                    proficiencyBonus: 0
                    bonus: 0
                    total: 0
                    sources:
                      - type: abilityModifier
                        value: 0
                initiative:
                  ability: DEX
                  abilityModifier: 2
                  bonus: 0
                  total: 2
                  sources:
                    - type: abilityModifier
                      ability: DEX
                      value: 2
                passivePerception:
                  skill: Perception
                  ability: WIS
                  base: 10
                  skillModifier: 1
                  bonus: 0
                  total: 11
                  sources:
                    - type: base
                      value: 10
                    - type: skillModifier
                      value: 1
                movement:
                  baseSpeed: 30
                  unit: ft
                  sources:
                    - type: species
                      name: Human
                      value: 30
                inventoryWeight:
                  total: 2
                  unit: lb
                  sources:
                    - equipmentId: 42
                      name: Shortbow
                      quantity: 1
                      weight: 2
                      total: 2
                spellcastingSummary:
                  canCastSpells: true
                  ability: INT
                  abilityModifier: 3
                  spellSaveDc: 13
                  spellAttackBonus: 5
                  selectedSpellsCount: 0
                  selectedCantripsCount: 1
                spellSlots:
                  - level: 1
                    max: 2
                    used: 0
                    available: 2
                selectedSpells:
                  - id: 1
                    name: Acid Splash
                    slug: acid-splash
                    level: 0
                    levelLabel: Cantrip
                    school: Evocation
                    castingTime: Action
                    range: 60 feet
                    components:
                      - V
                      - S
                    duration: Instantaneous
                    selectionType: cantrip
                currency:
                  cp: 0
                  sp: 0
                  ep: 0
                  gp: 8
                  pp: 0
                abilityScoreRules:
                  source: background
                  allowedChoices:
                    - CON
                    - INT
                    - WIS
                  bonusRules:
                    mode: standard_background
                    options:
                      - type: plus2_plus1
                        choices:
                          - bonus: 2
                            count: 1
                          - bonus: 1
                            count: 1
                            mustBeDifferentFromBonus: 2
                      - type: plus1_each_suggested
                        basedOn: abilityscores
                classDetails:
                  id: 12
                  name: Wizard
                  slug: wizard
                  description: A learned arcane scholar who studies the inner workings of magic to prepare spells, master rituals, and wield unmatched magical versatility.
                  role: caster
                  hitDie: 6
                  primaryAttributes:
                    - INT
                  recommendedSkills:
                    - Arcana
                    - Investigation
                    - History
                    - Nature
                    - Religion
                  savingThrows:
                    - INT
                    - WIS
                  spellcasting:
                    ability: INT
                    usesSpellbook: true
                    canCastRituals: true
                    selection:
                      mode: spellbook_plus_prepared
                      selectionType: prepared
                      changesWhen: long_rest
                      cantrips:
                        '1': 3
                      preparedSpells:
                        '1': 4
                      spellbookSpells:
                        '1': 6
                      spellsAddedPerLevel: 2
                  skillProficiencyChoices:
                    choose: 2
                    options:
                      - Arcana
                      - History
                      - Insight
                      - Investigation
                      - Medicine
                      - Religion
                  weaponProficiencies:
                    - Simple Weapons
                  armorTraining: []
                  startingEquipmentOptions:
                    - label: A
                      items:
                        - Quarterstaff
                        - Scholar's Pack
                        - Spellbook
                        - 5 GP
                    - label: B
                      items:
                        - Dagger
                        - Scholar's Pack
                        - Spellbook
                        - 5 GP
                  equipmentOptions:
                    - Quarterstaff, Scholar's Pack, Spellbook, and 5 GP
                    - Dagger, Scholar's Pack, Spellbook, and 5 GP
                  subclasses:
                    - Evoker
                  featuresByLevel: []
                speciesDetails:
                  id: 3
                  name: Elf
                  slug: elf
                  description: Elves are a magical people of otherworldly grace.
                  creatureType: Humanoid
                  size: Medium
                  speed: 30
                  specialTraits:
                    - name: Darkvision
                      description: You have Darkvision with a range of 60 feet.
                  subspecies: []
                backgroundDetails:
                  id: 13
                  name: Sage
                  slug: sage
                  description: You spent your formative years traveling between manors and monasteries, performing various odd jobs and services in exchange for access to their libraries.
                  abilityScores:
                    - CON
                    - INT
                    - WIS
                  feat: Magic Initiate (Wizard)
                  skillProficiencies:
                    - Arcana
                    - History
                  toolProficiency: Calligrapher's Supplies
                  equipmentOptions:
                    - Quarterstaff, Calligrapher's Supplies, Book (history), Parchment (8 sheets), Robe, 8 GP
                    - 50 GP
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Unauthorized
        '404':
          description: Character not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Character not found
        '500':
          description: Failed to fetch character detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to fetch character detail
    patch:
      tags:
        - Characters
      operationId: updateCharacter
      summary: Update character
      description: |
        Updates a single character owned by the authenticated owner and returns the same enriched detail shape, including `abilityScores`, `abilityModifiers`, `skillProficiencies`, `abilityScoreRules`, `classDetails`, `speciesDetails`, and `backgroundDetails` when those relations exist.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/CharacterId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CharacterUpdateRequestBody'
            examples:
              assignClass:
                summary: Assign wizard class
                value:
                  classId: 12
              updateNameAndLevel:
                summary: Update name and level
                value:
                  name: Merien the Wise
                  level: 2
              updateAbilityScores:
                summary: Update selected ability scores
                value:
                  abilityScores:
                    base:
                      STR: 8
                      DEX: 14
                      CON: 13
                      INT: 15
                      WIS: 12
                      CHA: 10
                    bonuses:
                      STR: 0
                      DEX: 0
                      CON: 0
                      INT: 2
                      WIS: 1
                      CHA: 0
              updateSkillProficiencies:
                summary: Update character skill proficiencies
                value:
                  skillProficiencies:
                    - Arcana
                    - History
      responses:
        '200':
          description: Character updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CharacterResponseBody'
        '400':
          description: Invalid character request payload
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Invalid character request payload
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Unauthorized
        '404':
          description: Character not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Character not found
        '500':
          description: Failed to update character
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to update character
    delete:
      tags:
        - Characters
      operationId: deleteCharacter
      summary: Delete character
      description: |
        Deletes a single character owned by the authenticated owner.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/CharacterId'
      responses:
        '200':
          description: Character deleted successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MessageResponse'
              example:
                message: Character deleted successfully
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Unauthorized
        '404':
          description: Character not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Character not found
        '500':
          description: Failed to delete character
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to delete character

  /api/characters/{id}/skills:
    get:
      tags:
        - Characters
      operationId: getCharacterSkills
      summary: Get calculated character skills
      description: |
        Returns the calculated skill list for the character based on:
        - current `skillProficiencies`
        - current level
        - derived ability modifiers

        This endpoint is a derived view, not a raw persisted payload.

        When the character has no saved ability scores yet, ability-based values are currently returned as `0`.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/CharacterId'
      responses:
        '200':
          description: Character skills returned successfully
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/CharacterSkillItem'
              example:
                - name: Arcana
                  ability: INT
                  isProficient: true
                  abilityModifier: 3
                  proficiencyBonus: 2
                  total: 5
                - name: Stealth
                  ability: DEX
                  isProficient: false
                  abilityModifier: 2
                  proficiencyBonus: 0
                  total: 2
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Unauthorized
        '404':
          description: Character not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Character not found
        '500':
          description: Failed to fetch character skills
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to fetch character skills

  /api/characters/{id}/equipment:
    get:
      tags:
        - Characters
      operationId: getCharacterEquipment
      summary: Get character equipment
      description: |
        Returns the character's current equipment list.

        If the character has no equipment, `equipment` is returned as an empty array.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/CharacterId'
      responses:
        '200':
          description: Character equipment returned successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CharacterEquipmentResponseBody'
              examples:
                empty:
                  summary: Character without equipment
                  value:
                    characterId: 101
                    equipment: []
                withEquipment:
                  summary: Character with equipment
                  value:
                    characterId: 101
                    equipment:
                      - id: 42
                        name: Longsword
                        category: Weapon
                        type: Weapon
                        quantity: 3
                        isEquipped: false
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Unauthorized
        '404':
          description: Character not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Character not found
        '500':
          description: Failed to fetch character equipment
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to fetch character equipment
    post:
      tags:
        - Characters
      operationId: addCharacterEquipment
      summary: Add character equipment
      description: |
        Adds equipment to the character and returns the updated character equipment list.

        If the same equipment already exists for the character, the API increments the existing quantity and updates `isEquipped` with the latest submitted value.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/CharacterId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CharacterEquipmentAddRequestBody'
            example:
              equipmentId: 42
              quantity: 1
              isEquipped: true
      responses:
        '201':
          description: Character equipment added successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CharacterEquipmentResponseBody'
              example:
                characterId: 101
                equipment:
                  - id: 42
                    name: Longsword
                    category: Weapon
                    type: Weapon
                    quantity: 1
                    isEquipped: true
        '400':
          description: Invalid character equipment request payload
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Invalid character equipment request payload
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Unauthorized
        '404':
          description: Character or equipment not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                characterNotFound:
                  summary: Character not found
                  value:
                    error: Character not found
                equipmentNotFound:
                  summary: Equipment not found
                  value:
                    error: Equipment not found
        '500':
          description: Failed to add character equipment
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to add character equipment

  /api/characters/{id}/equipment/class-choice:
    post:
      tags:
        - Characters
      operationId: resolveCharacterClassEquipmentChoice
      summary: Resolve class equipment package choice
      description: |
        Resolves a pending class equipment package and returns the updated equipment state.

        At least one of `optionLabel` or `optionIndex` must be present. When both are sent, `optionLabel` is used.

        Currency found in the selected package is added to `character.currency` and returned in `addedCurrency`.
        Currently unsupported variable items are skipped instead of failing the whole request.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/CharacterId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CharacterEquipmentPackageChoiceRequestBody'
            examples:
              byLabel:
                summary: Choose by label
                value:
                  optionLabel: A
              byIndex:
                summary: Choose by index
                value:
                  optionIndex: 0
      responses:
        '200':
          description: Class equipment package resolved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CharacterEquipmentPackageChoiceResponseBody'
              example:
                characterId: 101
                appliedChoice:
                  source: class
                  label: A
                  optionIndex: 0
                addedEquipment:
                  - id: 42
                    name: Greataxe
                    quantity: 1
                    isEquipped: true
                  - id: 43
                    name: Handaxe
                    quantity: 4
                    isEquipped: false
                addedCurrency:
                  cp: 0
                  sp: 0
                  ep: 0
                  gp: 15
                  pp: 0
                skippedItems: []
                pendingChoices:
                  - backgroundEquipmentSelection
                equipment:
                  - id: 42
                    name: Greataxe
                    category: Weapon
                    type: Weapon
                    quantity: 1
                    isEquipped: true
                  - id: 43
                    name: Handaxe
                    category: Weapon
                    type: Weapon
                    quantity: 4
                    isEquipped: false
        '400':
          description: Invalid payload or no pending class equipment selection
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                invalidPayload:
                  summary: Invalid payload
                  value:
                    error: Invalid character equipment choice payload
                noPendingChoice:
                  summary: No pending class choice
                  value:
                    error: No class equipment selection pending
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Unauthorized
        '404':
          description: Character or selected package option not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                characterNotFound:
                  summary: Character not found
                  value:
                    error: Character not found
                packageOptionNotFound:
                  summary: Package option not found
                  value:
                    error: Selected package option not found
        '500':
          description: Failed to resolve class equipment choice
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to resolve class equipment choice

  /api/characters/{id}/equipment/background-choice:
    post:
      tags:
        - Characters
      operationId: resolveCharacterBackgroundEquipmentChoice
      summary: Resolve background equipment package choice
      description: |
        Resolves a pending background equipment package and returns the updated equipment state.

        At least one of `optionLabel` or `optionIndex` must be present. When both are sent, `optionLabel` is used.

        Currency found in the selected package is added to `character.currency` and returned in `addedCurrency`.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/CharacterId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CharacterEquipmentPackageChoiceRequestBody'
            example:
              optionIndex: 0
      responses:
        '200':
          description: Background equipment package resolved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CharacterEquipmentPackageChoiceResponseBody'
              example:
                characterId: 101
                appliedChoice:
                  source: background
                  label: null
                  optionIndex: 0
                addedEquipment:
                  - id: 12
                    name: Spear
                    quantity: 1
                    isEquipped: true
                  - id: 42
                    name: Shortbow
                    quantity: 1
                    isEquipped: false
                  - id: 44
                    name: Arrows
                    quantity: 20
                    isEquipped: false
                  - id: 45
                    name: Quiver
                    quantity: 1
                    isEquipped: false
                addedCurrency:
                  cp: 0
                  sp: 0
                  ep: 0
                  gp: 14
                  pp: 0
                skippedItems:
                  - name: Gaming Set (same as above)
                    reason: Depends on a previous choice that is not handled by this endpoint
                pendingChoices: []
                equipment:
                  - id: 12
                    name: Spear
                    category: Weapon
                    type: Weapon
                    quantity: 1
                    isEquipped: true
                  - id: 42
                    name: Shortbow
                    category: Weapon
                    type: Weapon
                    quantity: 1
                    isEquipped: false
                  - id: 44
                    name: Arrows
                    category: Ammunition
                    type: Gear
                    quantity: 20
                    isEquipped: false
                  - id: 45
                    name: Quiver
                    category: Container
                    type: Gear
                    quantity: 1
                    isEquipped: false
        '400':
          description: Invalid payload or no pending background equipment selection
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                invalidPayload:
                  summary: Invalid payload
                  value:
                    error: Invalid character equipment choice payload
                noPendingChoice:
                  summary: No pending background choice
                  value:
                    error: No background equipment selection pending
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Unauthorized
        '404':
          description: Character or selected package option not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                characterNotFound:
                  summary: Character not found
                  value:
                    error: Character not found
                packageOptionNotFound:
                  summary: Package option not found
                  value:
                    error: Selected package option not found
        '500':
          description: Failed to resolve background equipment choice
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to resolve background equipment choice

  /api/characters/{id}/equipment/{equipmentId}:
    patch:
      tags:
        - Characters
      operationId: updateCharacterEquipment
      summary: Update character equipment
      description: |
        Updates an equipment item already attached to the character and returns the updated character equipment list.

        At least one of `quantity` or `isEquipped` must be present. `quantity` must be a positive integer.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/CharacterId'
        - $ref: '#/components/parameters/CharacterEquipmentId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CharacterEquipmentUpdateRequestBody'
            examples:
              updateQuantity:
                summary: Update quantity
                value:
                  quantity: 2
              updateEquippedState:
                summary: Update equipped state
                value:
                  isEquipped: true
              updateBoth:
                summary: Update quantity and equipped state
                value:
                  quantity: 2
                  isEquipped: false
      responses:
        '200':
          description: Character equipment updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CharacterEquipmentResponseBody'
              example:
                characterId: 101
                equipment:
                  - id: 42
                    name: Longsword
                    category: Weapon
                    type: Weapon
                    quantity: 2
                    isEquipped: true
        '400':
          description: Invalid character equipment request payload
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Invalid character equipment request payload
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Unauthorized
        '404':
          description: Character or character equipment not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                characterNotFound:
                  summary: Character not found
                  value:
                    error: Character not found
                characterEquipmentNotFound:
                  summary: Character equipment not found
                  value:
                    error: Character equipment not found
        '500':
          description: Failed to update character equipment
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to update character equipment
    delete:
      tags:
        - Characters
      operationId: removeCharacterEquipment
      summary: Remove character equipment
      description: |
        Removes an equipment item from the character and returns the updated character equipment list.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/CharacterId'
        - $ref: '#/components/parameters/CharacterEquipmentId'
      responses:
        '200':
          description: Character equipment removed successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CharacterEquipmentResponseBody'
              example:
                characterId: 101
                equipment: []
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Unauthorized
        '404':
          description: Character or character equipment not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                characterNotFound:
                  summary: Character not found
                  value:
                    error: Character not found
                characterEquipmentNotFound:
                  summary: Character equipment not found
                  value:
                    error: Character equipment not found
        '500':
          description: Failed to remove character equipment
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to remove character equipment

  /api/characters/{id}/ability-score-options:
    get:
      tags:
        - Characters
      operationId: getCharacterAbilityScoreOptions
      summary: Get character ability score options
      description: |
        Returns the current ability score selection state for the character based on the assigned background.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/CharacterId'
      responses:
        '200':
          description: Character ability score options returned successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CharacterAbilityScoreOptionsResponseBody'
              examples:
                noSelectionYet:
                  summary: Background selected but no ability scores saved yet
                  value:
                    characterId: 101
                    backgroundId: 13
                    backgroundName: Sage
                    selectionRules:
                      source: background
                      allowedChoices:
                        - CON
                        - INT
                        - WIS
                      bonusRules:
                        mode: standard_background
                        options:
                          - type: plus2_plus1
                            choices:
                              - bonus: 2
                                count: 1
                              - bonus: 1
                                count: 1
                                mustBeDifferentFromBonus: 2
                          - type: plus1_each_suggested
                            basedOn: abilityscores
                    selectedAbilityScores: null
                    availableChoices:
                      - CON
                      - INT
                      - WIS
                selectedScores:
                  summary: Ability scores already selected
                  value:
                    characterId: 101
                    backgroundId: 13
                    backgroundName: Sage
                    selectionRules:
                      source: background
                      allowedChoices:
                        - CON
                        - INT
                        - WIS
                      bonusRules:
                        mode: standard_background
                        options:
                          - type: plus2_plus1
                            choices:
                              - bonus: 2
                                count: 1
                              - bonus: 1
                                count: 1
                                mustBeDifferentFromBonus: 2
                          - type: plus1_each_suggested
                            basedOn: abilityscores
                    selectedAbilityScores:
                      base:
                        STR: 8
                        DEX: 14
                        CON: 13
                        INT: 15
                        WIS: 12
                        CHA: 10
                      bonuses:
                        STR: 0
                        DEX: 0
                        CON: 0
                        INT: 2
                        WIS: 1
                        CHA: 0
                      final:
                        STR: 8
                        DEX: 14
                        CON: 13
                        INT: 17
                        WIS: 13
                        CHA: 10
                    availableChoices:
                      - CON
                      - INT
                      - WIS
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Unauthorized
        '404':
          description: Character not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Character not found
        '500':
          description: Failed to fetch character ability score options
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to fetch character ability score options

  /api/characters/{id}/ability-scores:
    put:
      tags:
        - Characters
      operationId: updateCharacterAbilityScores
      summary: Update character ability scores
      description: |
        Persists the selected ability scores for the character and returns the updated ability score selection state.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/CharacterId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CharacterAbilityScoresUpdateRequestBody'
            example:
              abilityScores:
                base:
                  STR: 8
                  DEX: 14
                  CON: 13
                  INT: 15
                  WIS: 12
                  CHA: 10
                bonuses:
                  STR: 0
                  DEX: 0
                  CON: 0
                  INT: 2
                  WIS: 1
                  CHA: 0
      responses:
        '200':
          description: Character ability scores updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CharacterAbilityScoreOptionsResponseBody'
        '400':
          description: Invalid character ability scores payload or unavailable selection
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                invalidPayload:
                  summary: Invalid payload
                  value:
                    error: Invalid character ability scores payload
                unavailableSelection:
                  summary: Background does not support ability score selection
                  value:
                    error: Ability score selection is not available for this character
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Unauthorized
        '404':
          description: Character not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Character not found
        '500':
          description: Failed to update character ability scores
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to update character ability scores

  /api/characters/{id}/spell-options:
    get:
      tags:
        - Characters
      operationId: getCharacterSpellOptions
      summary: Get character spell options
      description: |
        Returns the spells available to the character based on the assigned class.
        If the character has no spellcasting class, `spells` is returned as an empty array.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/CharacterId'
      responses:
        '200':
          description: Character spell options returned successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CharacterSpellOptionsResponseBody'
              examples:
                nonCaster:
                  summary: Non-spellcasting class
                  value:
                    characterId: 101
                    classId: 1
                    className: Barbarian
                    spells: []
                wizard:
                  summary: Wizard spell options
                  value:
                    characterId: 101
                    classId: 12
                    className: Wizard
                    spells:
                      - id: 1
                        name: Acid Splash
                        level: 0
                        levelLabel: Cantrip
                      - id: 18
                        name: Alarm
                        level: 1
                        levelLabel: Level 1
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Unauthorized
        '404':
          description: Character not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Character not found
        '500':
          description: Failed to fetch character spell options
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to fetch character spell options

  /api/characters/{id}/spell-selection:
    get:
      tags:
        - Characters
      operationId: getCharacterSpellSelection
      summary: Get character spell selection state
      description: |
        Returns current selection rules, selected spells, and available spells for the character.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/CharacterId'
      responses:
        '200':
          description: Character spell selection returned successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CharacterSpellSelectionResponseBody'
              example:
                characterId: 101
                classId: 12
                className: Wizard
                level: 1
                selectionRules:
                  canSelectSpells: true
                  selectionType: prepared
                  maxCantrips: 3
                  maxSpells: 0
                selectedSpells: []
                availableSpells:
                  - id: 1
                    name: Acid Splash
                    level: 0
                    levelLabel: Cantrip
                  - id: 18
                    name: Alarm
                    level: 1
                    levelLabel: Level 1
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Unauthorized
        '404':
          description: Character not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Character not found
        '500':
          description: Failed to fetch character spell selection
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to fetch character spell selection

  /api/characters/{id}/spells:
    put:
      tags:
        - Characters
      operationId: updateCharacterSpells
      summary: Replace selected character spells
      description: |
        Replaces the character's selected spells based on the current spell selection rules.
      security:
        - bearerAuth: []
      parameters:
        - $ref: '#/components/parameters/CharacterId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CharacterSpellSelectionUpdateRequestBody'
            example:
              spellIds:
                - 1
                - 2
                - 3
      responses:
        '200':
          description: Character spells updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CharacterSpellSelectionResponseBody'
        '400':
          description: Invalid character spell selection payload or invalid spell selection
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              examples:
                invalidPayload:
                  summary: Invalid payload
                  value:
                    error: Invalid character spell selection payload
                duplicates:
                  summary: Duplicate spell ids
                  value:
                    error: Duplicate spell ids are not allowed
                unavailableSelection:
                  summary: Class cannot select spells
                  value:
                    error: Spell selection is not available for this character
                invalidSpell:
                  summary: Invalid spell id for character
                  value:
                    error: One or more spells are invalid for this character
                tooManyCantrips:
                  summary: Too many cantrips
                  value:
                    error: Too many cantrips selected
                tooManySpells:
                  summary: Too many leveled spells
                  value:
                    error: Too many spells selected
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Unauthorized
        '404':
          description: Character not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Character not found
        '500':
          description: Failed to update character spells
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                error: Failed to update character spells

components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT

  parameters:
    CharacterId:
      name: id
      in: path
      required: true
      description: Character numeric identifier.
      schema:
        type: integer
        minimum: 1

    CharacterEquipmentId:
      name: equipmentId
      in: path
      required: true
      description: Equipment numeric identifier attached to the character.
      schema:
        type: integer
        minimum: 1

  schemas:
    TokenRequestBody:
      type: object
      required:
        - username
        - password
      properties:
        username:
          type: string
          example: <your-username>
        password:
          type: string
          example: <your-password>

    TokenResponseBody:
      type: object
      required:
        - token
      properties:
        token:
          type: string
          example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwib3duZXJJZCI6MSwidXNlcm5hbWUiOiJkZW1vIiwiaWF0IjoxNzAwMDAwMDAwLCJleHAiOjE3MDAwMDM2MDB9.signature

    MessageResponse:
      type: object
      required:
        - message
      properties:
        message:
          type: string
          example: Character deleted successfully

    AttributeShortname:
      type: string
      enum:
        - STR
        - DEX
        - CON
        - INT
        - WIS
        - CHA
      example: STR

    SkillName:
      type: string
      enum:
        - Athletics
        - Acrobatics
        - Sleight of Hand
        - Stealth
        - Arcana
        - History
        - Investigation
        - Nature
        - Religion
        - Animal Handling
        - Insight
        - Medicine
        - Perception
        - Survival
        - Deception
        - Intimidation
        - Performance
        - Persuasion
      example: Athletics

    Attribute:
      type: object
      required:
        - id
        - name
        - shortname
        - description
        - skills
      properties:
        id:
          type: integer
          example: 1
        name:
          type: string
          example: Strength
        shortname:
          $ref: '#/components/schemas/AttributeShortname'
        description:
          type: string
          example: Measures physical power, carrying capacity, and effectiveness in brute-force actions such as lifting, pushing, and melee attacks.
        skills:
          type: array
          description: List of skill names associated with the attribute.
          items:
            $ref: '#/components/schemas/SkillName'
          example:
            - Athletics

    BackgroundListItem:
      type: object
      required:
        - id
        - name
      properties:
        id:
          type: integer
          example: 1
        name:
          type: string
          example: Acolyte

    BackgroundDetail:
      type: object
      required:
        - id
        - name
        - slug
        - description
        - abilityScores
        - feat
        - skillProficiencies
        - toolProficiency
        - equipmentOptions
      properties:
        id:
          type: integer
          example: 1
        name:
          type: string
          example: Acolyte
        slug:
          type: string
          example: acolyte
        description:
          type: string
          example: You devoted yourself to service in a temple, either nestled in a town or secluded in a sacred grove.
        abilityScores:
          type: array
          items:
            $ref: '#/components/schemas/AttributeShortname'
          example:
            - INT
            - WIS
            - CHA
        feat:
          type: string
          example: Magic Initiate (Cleric)
        skillProficiencies:
          type: array
          items:
            $ref: '#/components/schemas/SkillName'
          example:
            - Insight
            - Religion
        toolProficiency:
          type: string
          nullable: true
          example: Calligrapher's Supplies
        equipmentOptions:
          type: array
          items:
            type: string
          example:
            - Calligrapher's Supplies, Book (prayers), Holy Symbol, Parchment (10 sheets), Robe, 8 GP
            - 50 GP

    EquipmentListItem:
      type: object
      required:
        - id
        - name
        - category
        - type
        - cost
        - weight
        - isMagical
      properties:
        id:
          type: integer
          example: 1
        name:
          type: string
          example: Club
        category:
          type: string
          example: Weapon
        type:
          type: string
          example: Weapon
        cost:
          type: string
          example: 1 sp
        weight:
          type: number
          nullable: true
          example: 2
        isMagical:
          type: boolean
          example: false

    EquipmentDamageDie:
      type: object
      required:
        - count
        - value
      properties:
        count:
          type: integer
          example: 1
        value:
          type: integer
          example: 4

    EquipmentDamage:
      type: object
      required:
        - formula
        - dice
        - bonus
        - damageType
      properties:
        formula:
          type: string
          example: 1d4
        dice:
          type: array
          items:
            $ref: '#/components/schemas/EquipmentDamageDie'
        bonus:
          type: integer
          example: 0
        damageType:
          type: string
          example: Bludgeoning

    EquipmentRange:
      type: object
      required:
        - normal
        - long
        - unit
      properties:
        normal:
          type: integer
          nullable: true
          example: 20
        long:
          type: integer
          nullable: true
          example: 60
        unit:
          type: string
          enum:
            - ft
          example: ft

    EquipmentProperty:
      type: object
      required:
        - name
        - slug
      properties:
        name:
          type: string
          example: Thrown
        slug:
          type: string
          example: thrown
        range:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/EquipmentRange'
        ammunitionType:
          type: string
          nullable: true
          example: Arrow
        damage:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/EquipmentDamage'
        note:
          type: string
          nullable: true
          example: unless mounted

    EquipmentMastery:
      type: object
      required:
        - name
        - slug
      properties:
        name:
          type: string
          example: Slow
        slug:
          type: string
          example: slow

    EquipmentArmorClass:
      type: object
      required:
        - base
        - dexModifier
      properties:
        base:
          type: integer
          example: 16
        dexModifier:
          type: object
          required:
            - applies
            - max
          properties:
            applies:
              type: boolean
              example: false
            max:
              type: integer
              nullable: true
              example: null

    EquipmentModifier:
      type: object
      required:
        - type
        - value
        - target
        - condition
      properties:
        type:
          type: string
          enum:
            - attackBonus
            - damageBonus
            - armorClassBonus
            - abilityScoreBonus
            - skillBonus
            - savingThrowBonus
            - speedBonus
          example: armorClassBonus
        value:
          type: integer
          example: 1
        target:
          type: string
          nullable: true
          example: AC
        condition:
          type: string
          nullable: true
          example: while equipped

    EquipmentEffect:
      type: object
      required:
        - name
        - description
      properties:
        name:
          type: string
          example: Stealth Disadvantage
        description:
          type: string
          example: You have disadvantage on Dexterity (Stealth) checks while wearing this armor.

    EquipmentWeaponDetails:
      type: object
      required:
        - kind
        - weaponCategory
        - attackType
        - damage
        - versatileDamage
        - properties
        - mastery
        - range
        - proficiencyType
        - ammunitionType
      properties:
        kind:
          type: string
          enum:
            - weapon
        weaponCategory:
          type: string
          example: Simple
        attackType:
          type: string
          example: Melee
        damage:
          $ref: '#/components/schemas/EquipmentDamage'
        versatileDamage:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/EquipmentDamage'
        properties:
          type: array
          items:
            $ref: '#/components/schemas/EquipmentProperty'
        mastery:
          $ref: '#/components/schemas/EquipmentMastery'
        range:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/EquipmentRange'
        proficiencyType:
          type: string
          example: Simple Weapons
        ammunitionType:
          type: string
          nullable: true
          example: null

    EquipmentArmorDetails:
      type: object
      required:
        - kind
        - armorType
        - trainingType
        - armorClass
        - strengthRequirement
        - stealthDisadvantage
        - donTime
        - doffTime
      properties:
        kind:
          type: string
          enum:
            - armor
        armorType:
          type: string
          example: Heavy Armor
        trainingType:
          type: string
          example: Heavy Armor
        armorClass:
          $ref: '#/components/schemas/EquipmentArmorClass'
        strengthRequirement:
          type: integer
          nullable: true
          example: 13
        stealthDisadvantage:
          type: boolean
          example: true
        donTime:
          type: string
          example: 5 minutes
        doffTime:
          type: string
          example: 1 minute

    EquipmentShieldDetails:
      type: object
      required:
        - kind
        - trainingType
        - armorClassBonus
        - donTime
        - doffTime
      properties:
        kind:
          type: string
          enum:
            - shield
        trainingType:
          type: string
          example: Shield
        armorClassBonus:
          type: integer
          example: 2
        donTime:
          type: string
          example: 1 action
        doffTime:
          type: string
          example: 1 action

    EquipmentGenericDetails:
      type: object
      required:
        - kind
      properties:
        kind:
          type: string
          enum:
            - generic

    EquipmentDetails:
      oneOf:
        - $ref: '#/components/schemas/EquipmentWeaponDetails'
        - $ref: '#/components/schemas/EquipmentArmorDetails'
        - $ref: '#/components/schemas/EquipmentShieldDetails'
        - $ref: '#/components/schemas/EquipmentGenericDetails'

    EquipmentDetail:
      type: object
      required:
        - id
        - name
        - slug
        - category
        - type
        - description
        - cost
        - weight
        - isMagical
        - modifiers
        - effects
        - details
      properties:
        id:
          type: integer
          example: 1
        name:
          type: string
          example: Club
        slug:
          type: string
          example: club
        category:
          type: string
          example: Weapon
        type:
          type: string
          example: Weapon
        description:
          type: string
          example: A simple melee weapon.
        cost:
          type: string
          example: 1 sp
        weight:
          type: number
          nullable: true
          example: 2
        isMagical:
          type: boolean
          example: false
        modifiers:
          type: array
          items:
            $ref: '#/components/schemas/EquipmentModifier'
        effects:
          type: array
          items:
            $ref: '#/components/schemas/EquipmentEffect'
        details:
          $ref: '#/components/schemas/EquipmentDetails'

    SkillListItem:
      type: object
      required:
        - id
        - name
      properties:
        id:
          type: integer
          example: 1
        name:
          $ref: '#/components/schemas/SkillName'

    SkillDetail:
      type: object
      required:
        - id
        - name
        - attribute
        - description
        - exampleofuse
        - commonclasses
      properties:
        id:
          type: integer
          example: 1
        name:
          $ref: '#/components/schemas/SkillName'
        attribute:
          $ref: '#/components/schemas/AttributeShortname'
        description:
          type: string
          example: Represents physical capability in actions such as climbing, jumping, swimming, or forcing objects through strength.
        exampleofuse:
          type: string
          example: Used when a character tries to climb a castle wall or force open a heavy gate.
        commonclasses:
          type: array
          items:
            type: string
          example:
            - Fighter
            - Barbarian
            - Paladin
            - Ranger

    ClassRole:
      type: string
      enum:
        - melee
        - caster
        - support
        - stealth
        - hybrid
      example: caster

    ClassListItem:
      type: object
      required:
        - id
        - name
      properties:
        id:
          type: integer
          example: 12
        name:
          type: string
          example: Wizard

    ClassSpellcasting:
      type: object
      required:
        - ability
      properties:
        ability:
          $ref: '#/components/schemas/AttributeShortname'
        usesSpellbook:
          type: boolean
          example: true
        canCastRituals:
          type: boolean
          example: true
        selection:
          $ref: '#/components/schemas/ClassSpellSelection'

    ClassSpellSelectionType:
      type: string
      enum:
        - known
        - prepared
      example: prepared

    ClassSpellSelection:
      type: object
      required:
        - selectionType
      properties:
        selectionType:
          $ref: '#/components/schemas/ClassSpellSelectionType'
        cantrips:
          type: object
          additionalProperties:
            type: integer
          example:
            '1': 3
        spells:
          type: object
          additionalProperties:
            type: integer
          example:
            '1': 2
        preparedSpells:
          type: object
          additionalProperties:
            type: integer
          example:
            '1': 4
        spellbookSpells:
          type: object
          additionalProperties:
            type: integer
          example:
            '1': 6
        spellsAddedPerLevel:
          type: integer
          example: 2
        mode:
          type: string
          example: spellbook_plus_prepared
        changesWhen:
          type: string
          example: long_rest

    ClassFeatureItem:
      type: object
      required:
        - name
        - description
      properties:
        name:
          type: string
          example: Spellcasting
        description:
          type: string
          example: You can prepare and cast wizard spells.

    ClassLevelProgressionItem:
      type: object
      required:
        - level
        - features
      properties:
        level:
          type: integer
          example: 1
        features:
          type: array
          items:
            $ref: '#/components/schemas/ClassFeatureItem'

    ClassSkillProficiencyChoices:
      type: object
      required:
        - choose
        - options
      properties:
        choose:
          type: integer
          example: 2
        options:
          type: array
          items:
            $ref: '#/components/schemas/SkillName'
          example:
            - Animal Handling
            - Athletics
            - Intimidation
            - Nature
            - Perception
            - Survival

    ClassStartingEquipmentOption:
      type: object
      required:
        - label
        - items
      properties:
        label:
          type: string
          nullable: true
          example: A
        items:
          type: array
          items:
            type: string
          example:
            - Greataxe
            - 4 Handaxes
            - Explorer's Pack
            - 15 GP

    ClassDetail:
      type: object
      required:
        - id
        - name
        - slug
        - primaryattributes
        - recommendedskills
        - savingthrows
        - hitdie
        - role
        - description
        - spellcasting
        - skillProficiencyChoices
        - weaponProficiencies
        - armorTraining
        - startingEquipmentOptions
        - equipmentOptions
        - subclasses
        - levelprogression
      properties:
        id:
          type: integer
          example: 12
        name:
          type: string
          example: Wizard
        slug:
          type: string
          example: wizard
        primaryattributes:
          type: array
          items:
            $ref: '#/components/schemas/AttributeShortname'
          example:
            - INT
        recommendedskills:
          type: array
          items:
            $ref: '#/components/schemas/SkillName'
          example:
            - Arcana
            - Investigation
            - History
            - Nature
            - Religion
        savingthrows:
          type: array
          items:
            $ref: '#/components/schemas/AttributeShortname'
          example:
            - INT
            - WIS
        hitdie:
          type: integer
          example: 6
        role:
          $ref: '#/components/schemas/ClassRole'
        description:
          type: string
          example: A learned arcane scholar who studies the inner workings of magic to prepare spells, master rituals, and wield unmatched magical versatility.
        spellcasting:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/ClassSpellcasting'
        skillProficiencyChoices:
          $ref: '#/components/schemas/ClassSkillProficiencyChoices'
        weaponProficiencies:
          type: array
          items:
            type: string
          example:
            - Simple Weapons
            - Martial Weapons
        armorTraining:
          type: array
          items:
            type: string
          example:
            - Light Armor
            - Medium Armor
            - Shield
        startingEquipmentOptions:
          type: array
          items:
            $ref: '#/components/schemas/ClassStartingEquipmentOption'
        equipmentOptions:
          type: array
          items:
            type: string
          example:
            - Greataxe, 4 Handaxes, Explorer's Pack, and 15 GP
            - 75 GP
        subclasses:
          type: array
          items:
            type: string
          example:
            - Evoker
        levelprogression:
          type: array
          items:
            $ref: '#/components/schemas/ClassLevelProgressionItem'

    SpellListItem:
      type: object
      required:
        - id
        - name
        - level
        - levelLabel
      properties:
        id:
          type: integer
          example: 1
        name:
          type: string
          example: Acid Splash
        level:
          type: integer
          example: 0
        levelLabel:
          type: string
          example: Cantrip

    SpellComponents:
      type: object
      required:
        - verbal
        - somatic
        - material
        - materialDescription
      properties:
        verbal:
          type: boolean
          example: true
        somatic:
          type: boolean
          example: true
        material:
          type: boolean
          example: false
        materialDescription:
          type: string
          nullable: true
          example: null

    SpellScalingEntry:
      type: object
      required:
        - level
        - description
      properties:
        level:
          type: integer
          example: 5
        description:
          type: string
          example: damage increases to 2d6

    SpellScaling:
      type: object
      required:
        - entries
      properties:
        entries:
          type: array
          items:
            $ref: '#/components/schemas/SpellScalingEntry'

    SpellDetail:
      type: object
      required:
        - id
        - name
        - slug
        - source
        - school
        - level
        - levelLabel
        - castingTime
        - range
        - components
        - duration
        - description
        - classes
        - scaling
      properties:
        id:
          type: integer
          example: 18
        name:
          type: string
          example: Alarm
        slug:
          type: string
          example: alarm
        source:
          type: string
          example: Player's Handbook
        school:
          type: string
          example: Abjuration
        level:
          type: integer
          example: 1
        levelLabel:
          type: string
          example: Level 1
        castingTime:
          type: string
          example: 1 minute or Ritual
        range:
          type: string
          example: 30 feet
        components:
          $ref: '#/components/schemas/SpellComponents'
        duration:
          type: string
          example: 8 hours
        description:
          type: string
          example: You set an alarm against intrusion.
        classes:
          type: array
          items:
            type: string
          example:
            - Ranger
            - Wizard
        scaling:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/SpellScaling'

    SpeciesListItem:
      type: object
      required:
        - id
        - name
      properties:
        id:
          type: integer
          example: 1
        name:
          type: string
          example: Dragonborn

    SpeciesTrait:
      type: object
      required:
        - name
        - description
      properties:
        name:
          type: string
          example: Darkvision
        description:
          type: string
          example: You have Darkvision with a range of 60 feet.

    SpeciesSubspecies:
      type: object
      required:
        - name
        - slug
        - description
        - specialTraits
      properties:
        name:
          type: string
          example: High Elf
        slug:
          type: string
          example: high-elf
        description:
          type: string
          example: A magical elven lineage with an arcane cantrip.
        specialTraits:
          type: array
          items:
            $ref: '#/components/schemas/SpeciesTrait'

    SpeciesDetail:
      type: object
      required:
        - id
        - name
        - slug
        - description
        - creatureType
        - size
        - speed
        - specialTraits
        - subspecies
      properties:
        id:
          type: integer
          example: 1
        name:
          type: string
          example: Dragonborn
        slug:
          type: string
          example: dragonborn
        description:
          type: string
          example: Dragonborn look like wingless, bipedal dragons.
        creatureType:
          type: string
          example: Humanoid
        size:
          type: string
          example: Medium
        speed:
          type: integer
          example: 30
        specialTraits:
          type: array
          items:
            $ref: '#/components/schemas/SpeciesTrait'
        subspecies:
          type: array
          items:
            $ref: '#/components/schemas/SpeciesSubspecies'

    CharacterStatus:
      type: string
      enum:
        - draft
        - complete
      example: draft

    CharacterMissingField:
      type: string
      enum:
        - classId
        - speciesId
        - backgroundId
      example: classId

    CharacterPendingChoice:
      type: string
      enum:
        - classEquipmentSelection
        - backgroundEquipmentSelection
      example: classEquipmentSelection

    CharacterListItem:
      type: object
      required:
        - id
        - name
        - status
        - level
      properties:
        id:
          type: integer
          example: 101
        name:
          type: string
          example: Merien
        status:
          $ref: '#/components/schemas/CharacterStatus'
        level:
          type: integer
          example: 1

    CharacterAbilityScores:
      type: object
      required:
        - STR
        - DEX
        - CON
        - INT
        - WIS
        - CHA
      properties:
        STR:
          type: integer
          example: 15
        DEX:
          type: integer
          example: 14
        CON:
          type: integer
          example: 13
        INT:
          type: integer
          example: 12
        WIS:
          type: integer
          example: 10
        CHA:
          type: integer
          example: 8

    CharacterAbilityScoresInput:
      type: object
      required:
        - base
        - bonuses
      properties:
        base:
          $ref: '#/components/schemas/CharacterAbilityScores'
        bonuses:
          $ref: '#/components/schemas/CharacterAbilityScores'

    CharacterResolvedAbilityScores:
      type: object
      required:
        - base
        - bonuses
        - final
      properties:
        base:
          $ref: '#/components/schemas/CharacterAbilityScores'
        bonuses:
          $ref: '#/components/schemas/CharacterAbilityScores'
        final:
          $ref: '#/components/schemas/CharacterAbilityScores'

    CharacterAbilityModifiers:
      type: object
      required:
        - STR
        - DEX
        - CON
        - INT
        - WIS
        - CHA
      properties:
        STR:
          type: integer
          example: -1
        DEX:
          type: integer
          example: 2
        CON:
          type: integer
          example: 1
        INT:
          type: integer
          example: 3
        WIS:
          type: integer
          example: 1
        CHA:
          type: integer
          example: 0

    CharacterArmorClassSourceType:
      type: string
      enum:
        - base
        - armor
        - shield
        - class
      example: base

    CharacterArmorClassSource:
      type: object
      required:
        - name
        - type
        - value
      properties:
        name:
          type: string
          example: Base AC
        type:
          $ref: '#/components/schemas/CharacterArmorClassSourceType'
        value:
          type: integer
          example: 10

    CharacterArmorClass:
      type: object
      required:
        - total
        - base
        - dexModifierApplied
        - classBonus
        - shieldBonus
        - sources
      properties:
        total:
          type: integer
          example: 12
        base:
          type: integer
          example: 10
        dexModifierApplied:
          type: integer
          example: 2
        classBonus:
          type: integer
          example: 0
        shieldBonus:
          type: integer
          example: 0
        sources:
          type: array
          items:
            $ref: '#/components/schemas/CharacterArmorClassSource'
          example:
            - name: Base AC
              type: base
              value: 10

    CharacterCurrency:
      type: object
      required:
        - cp
        - sp
        - ep
        - gp
        - pp
      properties:
        cp:
          type: integer
          example: 0
        sp:
          type: integer
          example: 0
        ep:
          type: integer
          example: 0
        gp:
          type: integer
          example: 8
        pp:
          type: integer
          example: 0

    CharacterWeaponAttackDamage:
      type: object
      required:
        - formula
        - base
        - modifier
        - damageType
      properties:
        formula:
          type: string
          example: 1d6 + 2
        base:
          type: string
          example: 1d6
        modifier:
          type: integer
          example: 2
        damageType:
          type: string
          example: Piercing

    CharacterWeaponAttack:
      type: object
      required:
        - equipmentId
        - name
        - attackType
        - ability
        - isProficient
        - abilityModifier
        - proficiencyBonus
        - attackBonus
        - damage
        - properties
        - range
      properties:
        equipmentId:
          type: integer
          example: 42
        name:
          type: string
          example: Shortbow
        attackType:
          type: string
          example: ranged
        ability:
          $ref: '#/components/schemas/AttributeShortname'
        isProficient:
          type: boolean
          example: true
        abilityModifier:
          type: integer
          example: 2
        proficiencyBonus:
          type: integer
          example: 2
        attackBonus:
          type: integer
          example: 4
        damage:
          $ref: '#/components/schemas/CharacterWeaponAttackDamage'
        properties:
          type: array
          items:
            type: string
          example:
            - Ammunition
            - Two-Handed
        range:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/EquipmentRange'

    CharacterHitPoints:
      type: object
      required:
        - max
        - current
        - temporary
        - hitDie
        - conModifier
        - calculation
      properties:
        max:
          type: integer
          example: 7
        current:
          type: integer
          example: 7
        temporary:
          type: integer
          example: 0
        hitDie:
          type: integer
          example: 6
        conModifier:
          type: integer
          example: 1
        calculation:
          type: string
          example: 6 + 1

    CharacterSavingThrowSource:
      type: object
      required:
        - type
        - value
      properties:
        type:
          type: string
          enum:
            - abilityModifier
            - classProficiency
            - bonus
          example: abilityModifier
        value:
          type: integer
          example: 3

    CharacterSavingThrow:
      type: object
      required:
        - ability
        - isProficient
        - abilityModifier
        - proficiencyBonus
        - bonus
        - total
        - sources
      properties:
        ability:
          $ref: '#/components/schemas/AttributeShortname'
        isProficient:
          type: boolean
          example: true
        abilityModifier:
          type: integer
          example: 3
        proficiencyBonus:
          type: integer
          example: 2
        bonus:
          type: integer
          example: 0
        total:
          type: integer
          example: 5
        sources:
          type: array
          items:
            $ref: '#/components/schemas/CharacterSavingThrowSource'

    CharacterInitiativeSource:
      type: object
      required:
        - type
        - value
      properties:
        type:
          type: string
          enum:
            - abilityModifier
            - bonus
          example: abilityModifier
        ability:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/AttributeShortname'
        value:
          type: integer
          example: 2

    CharacterInitiative:
      type: object
      required:
        - ability
        - abilityModifier
        - bonus
        - total
        - sources
      properties:
        ability:
          type: string
          enum:
            - DEX
          example: DEX
        abilityModifier:
          type: integer
          example: 2
        bonus:
          type: integer
          example: 0
        total:
          type: integer
          example: 2
        sources:
          type: array
          items:
            $ref: '#/components/schemas/CharacterInitiativeSource'

    CharacterPassivePerceptionSource:
      type: object
      required:
        - type
        - value
      properties:
        type:
          type: string
          enum:
            - base
            - skillModifier
            - bonus
          example: skillModifier
        value:
          type: integer
          example: 1

    CharacterPassivePerception:
      type: object
      required:
        - skill
        - ability
        - base
        - skillModifier
        - bonus
        - total
        - sources
      properties:
        skill:
          type: string
          enum:
            - Perception
          example: Perception
        ability:
          type: string
          enum:
            - WIS
          example: WIS
        base:
          type: integer
          example: 10
        skillModifier:
          type: integer
          example: 1
        bonus:
          type: integer
          example: 0
        total:
          type: integer
          example: 11
        sources:
          type: array
          items:
            $ref: '#/components/schemas/CharacterPassivePerceptionSource'

    CharacterMovementSource:
      type: object
      required:
        - type
        - name
        - value
      properties:
        type:
          type: string
          enum:
            - species
          example: species
        name:
          type: string
          example: Human
        value:
          type: integer
          example: 30

    CharacterMovement:
      type: object
      required:
        - baseSpeed
        - unit
        - sources
      properties:
        baseSpeed:
          type: integer
          example: 30
        unit:
          type: string
          enum:
            - ft
          example: ft
        sources:
          type: array
          items:
            $ref: '#/components/schemas/CharacterMovementSource'

    CharacterInventoryWeightSource:
      type: object
      required:
        - equipmentId
        - name
        - quantity
        - weight
        - total
      properties:
        equipmentId:
          type: integer
          example: 42
        name:
          type: string
          example: Shortbow
        quantity:
          type: integer
          example: 1
        weight:
          type: number
          example: 2
        total:
          type: number
          example: 2

    CharacterInventoryWeight:
      type: object
      required:
        - total
        - unit
        - sources
      properties:
        total:
          type: number
          example: 2
        unit:
          type: string
          enum:
            - lb
          example: lb
        sources:
          type: array
          items:
            $ref: '#/components/schemas/CharacterInventoryWeightSource'

    CharacterAbilityScoreBonusChoice:
      type: object
      required:
        - bonus
        - count
      properties:
        bonus:
          type: integer
          example: 2
        count:
          type: integer
          example: 1
        mustBeDifferentFromBonus:
          type: integer
          example: 2

    CharacterAbilityScoreRuleOptionPlusTwoPlusOne:
      type: object
      required:
        - type
        - choices
      properties:
        type:
          type: string
          enum:
            - plus2_plus1
        choices:
          type: array
          items:
            $ref: '#/components/schemas/CharacterAbilityScoreBonusChoice'

    CharacterAbilityScoreRuleOptionPlusOneEachSuggested:
      type: object
      required:
        - type
        - basedOn
      properties:
        type:
          type: string
          enum:
            - plus1_each_suggested
        basedOn:
          type: string
          enum:
            - abilityscores

    CharacterAbilityScoreRuleOption:
      oneOf:
        - $ref: '#/components/schemas/CharacterAbilityScoreRuleOptionPlusTwoPlusOne'
        - $ref: '#/components/schemas/CharacterAbilityScoreRuleOptionPlusOneEachSuggested'

    CharacterAbilityScoreBonusRules:
      type: object
      required:
        - mode
        - options
      properties:
        mode:
          type: string
          enum:
            - standard_background
        options:
          type: array
          items:
            $ref: '#/components/schemas/CharacterAbilityScoreRuleOption'

    CharacterAbilityScoreRules:
      type: object
      required:
        - source
        - allowedChoices
        - bonusRules
      properties:
        source:
          type: string
          enum:
            - background
        allowedChoices:
          type: array
          items:
            $ref: '#/components/schemas/AttributeShortname'
          example:
            - CON
            - INT
            - WIS
        bonusRules:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/CharacterAbilityScoreBonusRules'

    CharacterCreateRequestBody:
      type: object
      required:
        - name
      properties:
        name:
          type: string
          example: Merien
        classId:
          type: integer
          nullable: true
          minimum: 1
          example: 12
        speciesId:
          type: integer
          nullable: true
          minimum: 1
          example: 1
        backgroundId:
          type: integer
          nullable: true
          minimum: 1
          example: 13
        level:
          type: integer
          nullable: true
          minimum: 1
          example: 1
        abilityScores:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/CharacterAbilityScoresInput'
        currency:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/CharacterCurrency'
        skillProficiencies:
          type: array
          items:
            $ref: '#/components/schemas/SkillName'
          example:
            - Arcana
            - History

    CharacterUpdateRequestBody:
      type: object
      properties:
        name:
          type: string
          example: Merien the Wise
        classId:
          type: integer
          nullable: true
          minimum: 1
          example: 12
        speciesId:
          type: integer
          nullable: true
          minimum: 1
          example: 1
        backgroundId:
          type: integer
          nullable: true
          minimum: 1
          example: 13
        level:
          type: integer
          nullable: true
          minimum: 1
          example: 2
        abilityScores:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/CharacterAbilityScoresInput'
        currency:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/CharacterCurrency'
        skillProficiencies:
          type: array
          items:
            $ref: '#/components/schemas/SkillName'
          example:
            - Arcana
            - History

    CharacterClassFeatureItem:
      type: object
      required:
        - name
        - description
      properties:
        name:
          type: string
          example: Spellcasting
        description:
          type: string
          example: You can prepare and cast wizard spells.

    CharacterClassProgressionItem:
      type: object
      required:
        - level
        - features
      properties:
        level:
          type: integer
          example: 1
        features:
          type: array
          items:
            $ref: '#/components/schemas/CharacterClassFeatureItem'

    CharacterClassDetails:
      type: object
      required:
        - id
        - name
        - slug
        - description
        - role
        - hitDie
        - primaryAttributes
        - recommendedSkills
        - savingThrows
        - spellcasting
        - skillProficiencyChoices
        - weaponProficiencies
        - armorTraining
        - startingEquipmentOptions
        - equipmentOptions
        - subclasses
        - featuresByLevel
      properties:
        id:
          type: integer
          example: 12
        name:
          type: string
          example: Wizard
        slug:
          type: string
          example: wizard
        description:
          type: string
          example: A learned arcane scholar who studies the inner workings of magic to prepare spells, master rituals, and wield unmatched magical versatility.
        role:
          type: string
          example: caster
        hitDie:
          type: integer
          example: 6
        primaryAttributes:
          type: array
          items:
            $ref: '#/components/schemas/AttributeShortname'
          example:
            - INT
        recommendedSkills:
          type: array
          items:
            $ref: '#/components/schemas/SkillName'
          example:
            - Arcana
            - Investigation
        savingThrows:
          type: array
          items:
            $ref: '#/components/schemas/AttributeShortname'
          example:
            - INT
            - WIS
        spellcasting:
          type: object
          nullable: true
          additionalProperties: true
          example:
            ability: INT
            usesSpellbook: true
            canCastRituals: true
        skillProficiencyChoices:
          $ref: '#/components/schemas/ClassSkillProficiencyChoices'
        weaponProficiencies:
          type: array
          items:
            type: string
          example:
            - Simple Weapons
        armorTraining:
          type: array
          items:
            type: string
          example: []
        startingEquipmentOptions:
          type: array
          items:
            $ref: '#/components/schemas/ClassStartingEquipmentOption'
        equipmentOptions:
          type: array
          items:
            type: string
          example:
            - Quarterstaff, Scholar's Pack, Spellbook, and 5 GP
            - Dagger, Scholar's Pack, Spellbook, and 5 GP
        subclasses:
          type: array
          items:
            type: string
          example:
            - Evoker
        featuresByLevel:
          type: array
          items:
            $ref: '#/components/schemas/CharacterClassProgressionItem'

    CharacterResponseBody:
      type: object
      required:
        - id
        - name
        - status
        - classId
        - speciesId
        - backgroundId
        - level
        - missingFields
        - pendingChoices
        - abilityScores
        - abilityModifiers
        - armorClass
        - weaponAttacks
        - hitPoints
        - savingThrows
        - initiative
        - passivePerception
        - movement
        - inventoryWeight
        - spellcastingSummary
        - spellSlots
        - selectedSpells
        - currency
        - skillProficiencies
        - abilityScoreRules
        - classDetails
        - speciesDetails
        - backgroundDetails
      properties:
        id:
          type: integer
          example: 101
        name:
          type: string
          example: Merien
        status:
          $ref: '#/components/schemas/CharacterStatus'
        classId:
          type: integer
          nullable: true
          example: 12
        speciesId:
          type: integer
          nullable: true
          example: null
        backgroundId:
          type: integer
          nullable: true
          example: null
        level:
          type: integer
          example: 1
        missingFields:
          type: array
          items:
            $ref: '#/components/schemas/CharacterMissingField'
          example:
            - speciesId
            - backgroundId
        pendingChoices:
          type: array
          items:
            $ref: '#/components/schemas/CharacterPendingChoice'
          example: []
        abilityScores:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/CharacterResolvedAbilityScores'
        abilityModifiers:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/CharacterAbilityModifiers'
        armorClass:
          $ref: '#/components/schemas/CharacterArmorClass'
        weaponAttacks:
          type: array
          items:
            $ref: '#/components/schemas/CharacterWeaponAttack'
        hitPoints:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/CharacterHitPoints'
        savingThrows:
          type: array
          items:
            $ref: '#/components/schemas/CharacterSavingThrow'
        initiative:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/CharacterInitiative'
        passivePerception:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/CharacterPassivePerception'
        movement:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/CharacterMovement'
        inventoryWeight:
          $ref: '#/components/schemas/CharacterInventoryWeight'
        spellcastingSummary:
          $ref: '#/components/schemas/CharacterSpellcastingSummary'
        spellSlots:
          type: array
          items:
            $ref: '#/components/schemas/CharacterSpellSlot'
        selectedSpells:
          type: array
          items:
            $ref: '#/components/schemas/CharacterSelectedSpellDetail'
        currency:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/CharacterCurrency'
        skillProficiencies:
          type: array
          items:
            $ref: '#/components/schemas/SkillName'
          example:
            - Arcana
            - History
        abilityScoreRules:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/CharacterAbilityScoreRules'
        classDetails:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/CharacterClassDetails'
        speciesDetails:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/SpeciesDetail'
        backgroundDetails:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/BackgroundDetail'

    CharacterSpellOptionItem:
      type: object
      required:
        - id
        - name
        - level
        - levelLabel
      properties:
        id:
          type: integer
          example: 1
        name:
          type: string
          example: Acid Splash
        level:
          type: integer
          example: 0
        levelLabel:
          type: string
          example: Cantrip

    CharacterSpellcastingSummary:
      type: object
      required:
        - canCastSpells
        - ability
        - abilityModifier
        - spellSaveDc
        - spellAttackBonus
        - selectedSpellsCount
        - selectedCantripsCount
      properties:
        canCastSpells:
          type: boolean
          example: true
        ability:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/AttributeShortname'
        abilityModifier:
          type: integer
          nullable: true
          example: 3
        spellSaveDc:
          type: integer
          nullable: true
          example: 13
        spellAttackBonus:
          type: integer
          nullable: true
          example: 5
        selectedSpellsCount:
          type: integer
          example: 0
        selectedCantripsCount:
          type: integer
          example: 1

    CharacterSpellSlot:
      type: object
      required:
        - level
        - max
        - used
        - available
      properties:
        level:
          type: integer
          example: 1
        max:
          type: integer
          example: 2
        used:
          type: integer
          example: 0
        available:
          type: integer
          example: 2

    CharacterSelectedSpellDetail:
      allOf:
        - $ref: '#/components/schemas/CharacterSelectedSpellItem'
        - type: object
          required:
            - slug
            - school
            - castingTime
            - range
            - components
            - duration
          properties:
            slug:
              type: string
              example: acid-splash
            school:
              type: string
              example: Evocation
            castingTime:
              type: string
              example: Action
            range:
              type: string
              example: 60 feet
            components:
              type: array
              items:
                type: string
              example:
                - V
                - S
            duration:
              type: string
              example: Instantaneous

    CharacterSkillItem:
      type: object
      required:
        - name
        - ability
        - isProficient
        - abilityModifier
        - proficiencyBonus
        - total
      properties:
        name:
          $ref: '#/components/schemas/SkillName'
        ability:
          $ref: '#/components/schemas/AttributeShortname'
        isProficient:
          type: boolean
          example: true
        abilityModifier:
          type: integer
          example: 3
        proficiencyBonus:
          type: integer
          example: 2
        total:
          type: integer
          example: 5

    CharacterEquipmentItem:
      type: object
      required:
        - id
        - name
        - category
        - type
        - quantity
        - isEquipped
      properties:
        id:
          type: integer
          example: 42
        name:
          type: string
          example: Longsword
        category:
          type: string
          example: Weapon
        type:
          type: string
          example: Weapon
        quantity:
          type: integer
          minimum: 1
          example: 1
        isEquipped:
          type: boolean
          example: true

    CharacterEquipmentResponseBody:
      type: object
      required:
        - characterId
        - equipment
      properties:
        characterId:
          type: integer
          example: 101
        equipment:
          type: array
          items:
            $ref: '#/components/schemas/CharacterEquipmentItem'

    CharacterEquipmentAddRequestBody:
      type: object
      required:
        - equipmentId
      properties:
        equipmentId:
          type: integer
          minimum: 1
          example: 42
        quantity:
          type: integer
          minimum: 1
          default: 1
          example: 1
        isEquipped:
          type: boolean
          default: false
          example: true

    CharacterEquipmentUpdateRequestBody:
      type: object
      description: At least one of `quantity` or `isEquipped` must be present.
      properties:
        quantity:
          type: integer
          minimum: 1
          example: 2
        isEquipped:
          type: boolean
          example: true
      minProperties: 1

    CharacterEquipmentChoiceSource:
      type: string
      enum:
        - class
        - background
      example: class

    CharacterEquipmentPackageChoiceRequestBody:
      type: object
      description: At least one of `optionLabel` or `optionIndex` must be present. When both are sent, `optionLabel` is used.
      properties:
        optionLabel:
          type: string
          example: A
        optionIndex:
          type: integer
          minimum: 0
          example: 0
      minProperties: 1

    CharacterEquipmentPackageChoiceAppliedOption:
      type: object
      required:
        - source
        - label
        - optionIndex
      properties:
        source:
          $ref: '#/components/schemas/CharacterEquipmentChoiceSource'
        label:
          type: string
          nullable: true
          example: A
        optionIndex:
          type: integer
          minimum: 0
          example: 0

    CharacterEquipmentPackageChoiceAddedItem:
      type: object
      required:
        - id
        - name
        - quantity
        - isEquipped
      properties:
        id:
          type: integer
          example: 42
        name:
          type: string
          example: Greataxe
        quantity:
          type: integer
          minimum: 1
          example: 1
        isEquipped:
          type: boolean
          example: true

    CharacterEquipmentPackageChoiceSkippedItem:
      type: object
      required:
        - name
        - reason
      properties:
        name:
          type: string
          example: 15 GP
        reason:
          type: string
          example: Currency is not handled by this endpoint

    CharacterEquipmentPackageChoiceResponseBody:
      type: object
      required:
        - characterId
        - appliedChoice
        - addedEquipment
        - addedCurrency
        - skippedItems
        - pendingChoices
        - equipment
      properties:
        characterId:
          type: integer
          example: 101
        appliedChoice:
          $ref: '#/components/schemas/CharacterEquipmentPackageChoiceAppliedOption'
        addedEquipment:
          type: array
          items:
            $ref: '#/components/schemas/CharacterEquipmentPackageChoiceAddedItem'
        addedCurrency:
          $ref: '#/components/schemas/CharacterCurrency'
        skippedItems:
          type: array
          items:
            $ref: '#/components/schemas/CharacterEquipmentPackageChoiceSkippedItem'
        pendingChoices:
          type: array
          items:
            $ref: '#/components/schemas/CharacterPendingChoice'
        equipment:
          type: array
          items:
            $ref: '#/components/schemas/CharacterEquipmentItem'

    CharacterAbilityScoreOptionsResponseBody:
      type: object
      required:
        - characterId
        - backgroundId
        - backgroundName
        - selectionRules
        - selectedAbilityScores
        - availableChoices
      properties:
        characterId:
          type: integer
          example: 101
        backgroundId:
          type: integer
          nullable: true
          example: 13
        backgroundName:
          type: string
          nullable: true
          example: Sage
        selectionRules:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/CharacterAbilityScoreRules'
        selectedAbilityScores:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/CharacterResolvedAbilityScores'
        availableChoices:
          type: array
          items:
            $ref: '#/components/schemas/AttributeShortname'
          example:
            - CON
            - INT
            - WIS

    CharacterAbilityScoresUpdateRequestBody:
      type: object
      required:
        - abilityScores
      properties:
        abilityScores:
          $ref: '#/components/schemas/CharacterAbilityScoresInput'

    CharacterSpellOptionsResponseBody:
      type: object
      required:
        - characterId
        - classId
        - className
        - spells
      properties:
        characterId:
          type: integer
          example: 101
        classId:
          type: integer
          nullable: true
          example: 12
        className:
          type: string
          nullable: true
          example: Wizard
        spells:
          type: array
          items:
            $ref: '#/components/schemas/CharacterSpellOptionItem'

    CharacterSpellSelectionType:
      type: string
      enum:
        - known
        - prepared
        - cantrip
      example: prepared

    CharacterSpellSelectionRule:
      type: object
      required:
        - canSelectSpells
        - selectionType
        - maxCantrips
        - maxSpells
      properties:
        canSelectSpells:
          type: boolean
          example: true
        selectionType:
          nullable: true
          allOf:
            - $ref: '#/components/schemas/CharacterSpellSelectionType'
        maxCantrips:
          type: integer
          example: 3
        maxSpells:
          type: integer
          example: 0

    CharacterSelectedSpellItem:
      allOf:
        - $ref: '#/components/schemas/CharacterSpellOptionItem'
        - type: object
          required:
            - selectionType
          properties:
            selectionType:
              $ref: '#/components/schemas/CharacterSpellSelectionType'

    CharacterSpellSelectionResponseBody:
      type: object
      required:
        - characterId
        - classId
        - className
        - level
        - selectionRules
        - selectedSpells
        - availableSpells
      properties:
        characterId:
          type: integer
          example: 101
        classId:
          type: integer
          nullable: true
          example: 12
        className:
          type: string
          nullable: true
          example: Wizard
        level:
          type: integer
          example: 1
        selectionRules:
          $ref: '#/components/schemas/CharacterSpellSelectionRule'
        selectedSpells:
          type: array
          items:
            $ref: '#/components/schemas/CharacterSelectedSpellItem'
        availableSpells:
          type: array
          items:
            $ref: '#/components/schemas/CharacterSpellOptionItem'

    CharacterSpellSelectionUpdateRequestBody:
      type: object
      required:
        - spellIds
      properties:
        spellIds:
          type: array
          items:
            type: integer
            minimum: 1
          example:
            - 1
            - 2
            - 3

    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          type: string
          example: Skill not found
