---
openapi: 3.0.3
info:
  title: Sure API
  version: v1
  description: OpenAPI documentation generated from executable request specs.
servers:
- url: https://app.sure.am
  description: Production
- url: http://localhost:3000
  description: Local development
components:
  securitySchemes:
    apiKeyAuth:
      type: apiKey
      name: X-Api-Key
      in: header
      description: API key for authentication. Generate one from your account settings.
  schemas:
    Pagination:
      type: object
      required:
      - page
      - per_page
      - total_count
      - total_pages
      properties:
        page:
          type: integer
          minimum: 1
        per_page:
          type: integer
          minimum: 1
        total_count:
          type: integer
          minimum: 0
        total_pages:
          type: integer
          minimum: 0
    ErrorResponse:
      type: object
      required:
      - error
      properties:
        error:
          type: string
        message:
          type: string
          nullable: true
        details:
          oneOf:
          - type: array
            items:
              type: string
          - type: object
          nullable: true
        errors:
          type: array
          items:
            type: string
          nullable: true
          description: Validation error messages (alternative to details used by trades,
            valuations, etc.)
    ToolCall:
      type: object
      required:
      - id
      - function_name
      - function_arguments
      - created_at
      properties:
        id:
          type: string
          format: uuid
        function_name:
          type: string
        function_arguments:
          type: object
          additionalProperties: true
        function_result:
          type: object
          additionalProperties: true
          nullable: true
        created_at:
          type: string
          format: date-time
    Message:
      type: object
      required:
      - id
      - type
      - role
      - content
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        type:
          type: string
          enum:
          - user_message
          - assistant_message
        role:
          type: string
          enum:
          - user
          - assistant
        content:
          type: string
        model:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        tool_calls:
          type: array
          items:
            "$ref": "#/components/schemas/ToolCall"
          nullable: true
    MessageResponse:
      allOf:
      - "$ref": "#/components/schemas/Message"
      - type: object
        required:
        - chat_id
        properties:
          chat_id:
            type: string
            format: uuid
          ai_response_status:
            type: string
            enum:
            - pending
            - complete
            - failed
            nullable: true
          ai_response_message:
            type: string
            nullable: true
    ChatResource:
      type: object
      required:
      - id
      - title
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
        error:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    ChatSummary:
      allOf:
      - "$ref": "#/components/schemas/ChatResource"
      - type: object
        required:
        - message_count
        properties:
          message_count:
            type: integer
            minimum: 0
          last_message_at:
            type: string
            format: date-time
            nullable: true
    ChatDetail:
      allOf:
      - "$ref": "#/components/schemas/ChatResource"
      - type: object
        required:
        - messages
        properties:
          messages:
            type: array
            items:
              "$ref": "#/components/schemas/Message"
          pagination:
            "$ref": "#/components/schemas/Pagination"
            nullable: true
    ChatCollection:
      type: object
      required:
      - chats
      - pagination
      properties:
        chats:
          type: array
          items:
            "$ref": "#/components/schemas/ChatSummary"
        pagination:
          "$ref": "#/components/schemas/Pagination"
    RetryResponse:
      type: object
      required:
      - message
      - message_id
      properties:
        message:
          type: string
        message_id:
          type: string
          format: uuid
    Account:
      type: object
      required:
      - id
      - name
      - account_type
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        account_type:
          type: string
    AccountDetail:
      type: object
      required:
      - id
      - name
      - balance
      - currency
      - classification
      - account_type
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        balance:
          type: string
        currency:
          type: string
        classification:
          type: string
        account_type:
          type: string
    AccountCollection:
      type: object
      required:
      - accounts
      - pagination
      properties:
        accounts:
          type: array
          items:
            "$ref": "#/components/schemas/AccountDetail"
        pagination:
          "$ref": "#/components/schemas/Pagination"
    Category:
      type: object
      required:
      - id
      - name
      - classification
      - color
      - icon
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        classification:
          type: string
        color:
          type: string
        icon:
          type: string
    CategoryParent:
      type: object
      required:
      - id
      - name
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
    CategoryDetail:
      type: object
      required:
      - id
      - name
      - classification
      - color
      - icon
      - subcategories_count
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        classification:
          type: string
          enum:
          - income
          - expense
        color:
          type: string
        icon:
          type: string
        parent:
          "$ref": "#/components/schemas/CategoryParent"
          nullable: true
        subcategories_count:
          type: integer
          minimum: 0
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    CategoryCollection:
      type: object
      required:
      - categories
      - pagination
      properties:
        categories:
          type: array
          items:
            "$ref": "#/components/schemas/CategoryDetail"
        pagination:
          "$ref": "#/components/schemas/Pagination"
    Merchant:
      type: object
      required:
      - id
      - name
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
    MerchantDetail:
      type: object
      required:
      - id
      - name
      - type
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        type:
          type: string
          enum:
          - FamilyMerchant
          - ProviderMerchant
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    Tag:
      type: object
      required:
      - id
      - name
      - color
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        color:
          type: string
    TagDetail:
      type: object
      required:
      - id
      - name
      - color
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        color:
          type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    TagCollection:
      type: array
      items:
        "$ref": "#/components/schemas/TagDetail"
    Transfer:
      type: object
      required:
      - id
      - amount
      - currency
      properties:
        id:
          type: string
          format: uuid
        amount:
          type: string
        currency:
          type: string
        other_account:
          "$ref": "#/components/schemas/Account"
          nullable: true
    Transaction:
      type: object
      required:
      - id
      - date
      - amount
      - currency
      - name
      - classification
      - account
      - tags
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        date:
          type: string
          format: date
        amount:
          type: string
        currency:
          type: string
        name:
          type: string
        notes:
          type: string
          nullable: true
        classification:
          type: string
        account:
          "$ref": "#/components/schemas/Account"
        category:
          "$ref": "#/components/schemas/Category"
          nullable: true
        merchant:
          "$ref": "#/components/schemas/Merchant"
          nullable: true
        tags:
          type: array
          items:
            "$ref": "#/components/schemas/Tag"
        transfer:
          "$ref": "#/components/schemas/Transfer"
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    TransactionCollection:
      type: object
      required:
      - transactions
      - pagination
      properties:
        transactions:
          type: array
          items:
            "$ref": "#/components/schemas/Transaction"
        pagination:
          "$ref": "#/components/schemas/Pagination"
    Valuation:
      type: object
      required:
      - id
      - date
      - amount
      - currency
      - kind
      - account
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        date:
          type: string
          format: date
        amount:
          type: string
        currency:
          type: string
        notes:
          type: string
          nullable: true
        kind:
          type: string
        account:
          "$ref": "#/components/schemas/Account"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    DeleteResponse:
      type: object
      required:
      - message
      properties:
        message:
          type: string
    SuccessMessage:
      type: object
      required:
      - message
      properties:
        message:
          type: string
    ImportConfiguration:
      type: object
      properties:
        date_col_label:
          type: string
          nullable: true
        amount_col_label:
          type: string
          nullable: true
        name_col_label:
          type: string
          nullable: true
        category_col_label:
          type: string
          nullable: true
        tags_col_label:
          type: string
          nullable: true
        notes_col_label:
          type: string
          nullable: true
        account_col_label:
          type: string
          nullable: true
        date_format:
          type: string
          nullable: true
        number_format:
          type: string
          nullable: true
        signage_convention:
          type: string
          nullable: true
    ImportStats:
      type: object
      properties:
        rows_count:
          type: integer
          minimum: 0
        valid_rows_count:
          type: integer
          minimum: 0
          nullable: true
    ImportSummary:
      type: object
      required:
      - id
      - type
      - status
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        type:
          type: string
          enum:
          - TransactionImport
          - TradeImport
          - AccountImport
          - MintImport
          - CategoryImport
          - RuleImport
        status:
          type: string
          enum:
          - pending
          - complete
          - importing
          - reverting
          - revert_failed
          - failed
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        account_id:
          type: string
          format: uuid
          nullable: true
        rows_count:
          type: integer
          minimum: 0
        error:
          type: string
          nullable: true
    ImportDetail:
      type: object
      required:
      - id
      - type
      - status
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        type:
          type: string
          enum:
          - TransactionImport
          - TradeImport
          - AccountImport
          - MintImport
          - CategoryImport
          - RuleImport
        status:
          type: string
          enum:
          - pending
          - complete
          - importing
          - reverting
          - revert_failed
          - failed
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        account_id:
          type: string
          format: uuid
          nullable: true
        error:
          type: string
          nullable: true
        configuration:
          "$ref": "#/components/schemas/ImportConfiguration"
        stats:
          "$ref": "#/components/schemas/ImportStats"
    ImportCollection:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/ImportSummary"
        meta:
          type: object
          required:
          - current_page
          - total_pages
          - total_count
          - per_page
          properties:
            current_page:
              type: integer
              minimum: 1
            next_page:
              type: integer
              nullable: true
            prev_page:
              type: integer
              nullable: true
            total_pages:
              type: integer
              minimum: 0
            total_count:
              type: integer
              minimum: 0
            per_page:
              type: integer
              minimum: 1
    ImportResponse:
      type: object
      required:
      - data
      properties:
        data:
          "$ref": "#/components/schemas/ImportDetail"
    Trade:
      type: object
      required:
      - id
      - date
      - amount
      - currency
      - name
      - qty
      - price
      - account
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        date:
          type: string
          format: date
        amount:
          type: string
        currency:
          type: string
        name:
          type: string
        notes:
          type: string
          nullable: true
        qty:
          type: string
        price:
          type: string
        investment_activity_label:
          type: string
          nullable: true
        account:
          "$ref": "#/components/schemas/Account"
        security:
          type: object
          nullable: true
          properties:
            id:
              type: string
              format: uuid
            ticker:
              type: string
            name:
              type: string
              nullable: true
        category:
          type: object
          nullable: true
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    TradeCollection:
      type: object
      required:
      - trades
      - pagination
      properties:
        trades:
          type: array
          items:
            "$ref": "#/components/schemas/Trade"
        pagination:
          "$ref": "#/components/schemas/Pagination"
    Holding:
      type: object
      required:
      - id
      - date
      - qty
      - price
      - amount
      - currency
      - account
      - security
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        date:
          type: string
          format: date
        qty:
          type: string
          description: Quantity of shares held
        price:
          type: string
          description: Formatted price per share
        amount:
          type: string
        currency:
          type: string
        cost_basis_source:
          type: string
          nullable: true
        account:
          "$ref": "#/components/schemas/Account"
        security:
          type: object
          required:
          - id
          - ticker
          - name
          properties:
            id:
              type: string
              format: uuid
            ticker:
              type: string
            name:
              type: string
              nullable: true
        avg_cost:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    HoldingCollection:
      type: object
      required:
      - holdings
      - pagination
      properties:
        holdings:
          type: array
          items:
            "$ref": "#/components/schemas/Holding"
        pagination:
          "$ref": "#/components/schemas/Pagination"
paths:
  "/api/v1/merchants":
    get:
      summary: List merchants
      tags:
      - Merchants
      security:
      - apiKeyAuth: []
      responses:
        '200':
          description: merchants listed
          content:
            application/json:
              schema:
                type: array
                items:
                  "$ref": "#/components/schemas/MerchantDetail"
  "/api/v1/merchants/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: Merchant ID
      schema:
        type: string
    get:
      summary: Retrieve a merchant
      tags:
      - Merchants
      security:
      - apiKeyAuth: []
      responses:
        '200':
          description: merchant retrieved
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/MerchantDetail"
        '404':
          description: merchant not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/accounts":
    get:
      summary: List accounts
      tags:
      - Accounts
      security:
      - apiKeyAuth: []
      parameters:
      - name: page
        in: query
        required: false
        description: 'Page number (default: 1)'
        schema:
          type: integer
      - name: per_page
        in: query
        required: false
        description: 'Items per page (default: 25, max: 100)'
        schema:
          type: integer
      responses:
        '200':
          description: accounts paginated
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/AccountCollection"
  "/api/v1/auth/signup":
    post:
      summary: Sign up a new user
      tags:
      - Auth
      parameters: []
      responses:
        '201':
          description: user created
          content:
            application/json:
              schema:
                type: object
                properties:
                  access_token:
                    type: string
                  refresh_token:
                    type: string
                  token_type:
                    type: string
                  expires_in:
                    type: integer
                  created_at:
                    type: integer
                  user:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      email:
                        type: string
                      first_name:
                        type: string
                      last_name:
                        type: string
                      ui_layout:
                        type: string
                        enum:
                        - dashboard
                        - intro
                      ai_enabled:
                        type: boolean
        '422':
          description: validation error
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: invite code required or invalid
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                user:
                  type: object
                  properties:
                    email:
                      type: string
                      format: email
                      description: User email address
                    password:
                      type: string
                      description: Password (min 8 chars, mixed case, number, special
                        char)
                    first_name:
                      type: string
                    last_name:
                      type: string
                  required:
                  - email
                  - password
                device:
                  type: object
                  properties:
                    device_id:
                      type: string
                      description: Unique device identifier
                    device_name:
                      type: string
                      description: Human-readable device name
                    device_type:
                      type: string
                      description: Device type (e.g. ios, android)
                    os_version:
                      type: string
                    app_version:
                      type: string
                  required:
                  - device_id
                  - device_name
                  - device_type
                  - os_version
                  - app_version
                invite_code:
                  type: string
                  nullable: true
                  description: Invite code (required when invites are enforced)
              required:
              - user
              - device
        required: true
  "/api/v1/auth/login":
    post:
      summary: Log in with email and password
      tags:
      - Auth
      parameters: []
      responses:
        '200':
          description: login successful
          content:
            application/json:
              schema:
                type: object
                properties:
                  access_token:
                    type: string
                  refresh_token:
                    type: string
                  token_type:
                    type: string
                  expires_in:
                    type: integer
                  created_at:
                    type: integer
                  user:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      email:
                        type: string
                      first_name:
                        type: string
                      last_name:
                        type: string
                      ui_layout:
                        type: string
                        enum:
                        - dashboard
                        - intro
                      ai_enabled:
                        type: boolean
        '401':
          description: invalid credentials or MFA required
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                email:
                  type: string
                  format: email
                password:
                  type: string
                otp_code:
                  type: string
                  nullable: true
                  description: TOTP code if MFA is enabled
                device:
                  type: object
                  properties:
                    device_id:
                      type: string
                    device_name:
                      type: string
                    device_type:
                      type: string
                    os_version:
                      type: string
                    app_version:
                      type: string
                  required:
                  - device_id
                  - device_name
                  - device_type
                  - os_version
                  - app_version
              required:
              - email
              - password
              - device
        required: true
  "/api/v1/auth/sso_exchange":
    post:
      summary: Exchange mobile SSO authorization code for tokens
      tags:
      - Auth
      description: Exchanges a one-time authorization code (received via deep link
        after mobile SSO) for OAuth tokens. The code is single-use and expires after
        5 minutes.
      parameters: []
      responses:
        '200':
          description: tokens issued
          content:
            application/json:
              schema:
                type: object
                properties:
                  access_token:
                    type: string
                  refresh_token:
                    type: string
                  token_type:
                    type: string
                  expires_in:
                    type: integer
                  created_at:
                    type: integer
                  user:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      email:
                        type: string
                      first_name:
                        type: string
                      last_name:
                        type: string
                      ui_layout:
                        type: string
                        enum:
                        - dashboard
                        - intro
                      ai_enabled:
                        type: boolean
        '401':
          description: invalid or expired code
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                code:
                  type: string
                  description: One-time authorization code from mobile SSO callback
              required:
              - code
        required: true
  "/api/v1/auth/refresh":
    post:
      summary: Refresh an access token
      tags:
      - Auth
      parameters: []
      responses:
        '200':
          description: token refreshed
          content:
            application/json:
              schema:
                type: object
                properties:
                  access_token:
                    type: string
                  refresh_token:
                    type: string
                  token_type:
                    type: string
                  expires_in:
                    type: integer
                  created_at:
                    type: integer
        '401':
          description: invalid refresh token
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '400':
          description: missing refresh token
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                refresh_token:
                  type: string
                  description: The refresh token from a previous login or refresh
                device:
                  type: object
                  properties:
                    device_id:
                      type: string
                  required:
                  - device_id
              required:
              - refresh_token
              - device
        required: true
  "/api/v1/auth/enable_ai":
    patch:
      summary: Enable AI features for the authenticated user
      tags:
      - Auth
      security:
      - apiKeyAuth: []
      responses:
        '200':
          description: ai enabled
          content:
            application/json:
              schema:
                type: object
                properties:
                  user:
                    type: object
                    properties:
                      id:
                        type: string
                        format: uuid
                      email:
                        type: string
                      first_name:
                        type: string
                        nullable: true
                      last_name:
                        type: string
                        nullable: true
                      ui_layout:
                        type: string
                        enum:
                        - dashboard
                        - intro
                      ai_enabled:
                        type: boolean
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/categories":
    get:
      summary: List categories
      tags:
      - Categories
      security:
      - apiKeyAuth: []
      parameters:
      - name: page
        in: query
        required: false
        description: 'Page number (default: 1)'
        schema:
          type: integer
      - name: per_page
        in: query
        required: false
        description: 'Items per page (default: 25, max: 100)'
        schema:
          type: integer
      - name: classification
        in: query
        required: false
        description: Filter by classification (income or expense)
        schema:
          type: string
          enum:
          - income
          - expense
      - name: roots_only
        in: query
        required: false
        description: Return only root categories (no parent)
        schema:
          type: boolean
      - name: parent_id
        in: query
        required: false
        description: Filter by parent category ID
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: categories filtered by parent
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/CategoryCollection"
  "/api/v1/categories/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: Category ID
      schema:
        type: string
    get:
      summary: Retrieve a category
      tags:
      - Categories
      security:
      - apiKeyAuth: []
      responses:
        '200':
          description: subcategory retrieved with parent
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/CategoryDetail"
        '404':
          description: category not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/chats":
    get:
      summary: List chats
      tags:
      - Chats
      security:
      - apiKeyAuth: []
      responses:
        '200':
          description: chats listed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ChatCollection"
        '403':
          description: AI features disabled
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
    post:
      summary: Create chat
      tags:
      - Chats
      security:
      - apiKeyAuth: []
      parameters: []
      responses:
        '201':
          description: chat created
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ChatDetail"
        '422':
          description: validation error
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                  example: Monthly budget review
                message:
                  type: string
                  description: Optional initial message in the chat
                model:
                  type: string
                  description: Optional OpenAI model identifier
              required:
              - title
        required: true
  "/api/v1/chats/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: Chat ID
      schema:
        type: string
    get:
      summary: Retrieve a chat
      tags:
      - Chats
      security:
      - apiKeyAuth: []
      responses:
        '200':
          description: chat retrieved
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ChatDetail"
        '404':
          description: chat not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
    patch:
      summary: Update a chat
      tags:
      - Chats
      security:
      - apiKeyAuth: []
      parameters: []
      responses:
        '200':
          description: chat updated
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ChatDetail"
        '404':
          description: chat not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: validation error
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                  example: Updated chat title
        required: true
    delete:
      summary: Delete a chat
      tags:
      - Chats
      security:
      - apiKeyAuth: []
      responses:
        '204':
          description: chat deleted
        '404':
          description: chat not found
  "/api/v1/chats/{chat_id}/messages":
    parameters:
    - name: chat_id
      in: path
      required: true
      description: Chat ID
      schema:
        type: string
    post:
      summary: Create a message
      tags:
      - Chat Messages
      security:
      - apiKeyAuth: []
      parameters: []
      responses:
        '201':
          description: message created
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/MessageResponse"
        '404':
          description: chat not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: validation error
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                content:
                  type: string
                model:
                  type: string
              required:
              - content
        required: true
  "/api/v1/chats/{chat_id}/messages/retry":
    parameters:
    - name: chat_id
      in: path
      required: true
      description: Chat ID
      schema:
        type: string
    post:
      summary: Retry the last assistant response
      tags:
      - Chat Messages
      security:
      - apiKeyAuth: []
      responses:
        '202':
          description: retry started
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/RetryResponse"
        '404':
          description: chat not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: no assistant message available
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/holdings":
    get:
      summary: List holdings
      tags:
      - Holdings
      security:
      - apiKeyAuth: []
      parameters:
      - name: page
        in: query
        required: false
        description: 'Page number (default: 1)'
        schema:
          type: integer
      - name: per_page
        in: query
        required: false
        description: 'Items per page (default: 25, max: 100)'
        schema:
          type: integer
      - name: account_id
        in: query
        required: false
        description: Filter by account ID
        schema:
          type: string
      - name: account_ids
        in: query
        required: false
        description: Filter by multiple account IDs
        schema:
          type: array
          items:
            type: string
      - name: date
        in: query
        required: false
        description: Filter by exact date
        schema:
          type: string
          format: date
      - name: start_date
        in: query
        required: false
        description: Filter holdings from this date (inclusive)
        schema:
          type: string
          format: date
      - name: end_date
        in: query
        required: false
        description: Filter holdings until this date (inclusive)
        schema:
          type: string
          format: date
      - name: security_id
        in: query
        required: false
        description: Filter by security ID
        schema:
          type: string
      responses:
        '200':
          description: holdings paginated
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/HoldingCollection"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: invalid date filter
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/holdings/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: Holding ID
      schema:
        type: string
    get:
      summary: Retrieve holding
      tags:
      - Holdings
      security:
      - apiKeyAuth: []
      responses:
        '200':
          description: holding retrieved
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Holding"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: holding not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/imports":
    get:
      summary: List imports
      description: List all imports for the user's family with pagination and filtering.
      tags:
      - Imports
      security:
      - apiKeyAuth: []
      parameters:
      - name: page
        in: query
        required: false
        description: 'Page number (default: 1)'
        schema:
          type: integer
      - name: per_page
        in: query
        required: false
        description: 'Items per page (default: 25, max: 100)'
        schema:
          type: integer
      - name: status
        in: query
        required: false
        description: Filter by status
        schema:
          type: string
          enum:
          - pending
          - complete
          - importing
          - reverting
          - revert_failed
          - failed
      - name: type
        in: query
        required: false
        description: Filter by import type
        schema:
          type: string
          enum:
          - TransactionImport
          - TradeImport
          - AccountImport
          - MintImport
          - CategoryImport
          - RuleImport
      responses:
        '200':
          description: imports filtered by type
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ImportCollection"
    post:
      summary: Create import
      description: Create a new import from raw CSV content.
      tags:
      - Imports
      security:
      - apiKeyAuth: []
      parameters: []
      responses:
        '201':
          description: import created
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ImportResponse"
        '422':
          description: validation error - file too large
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                raw_file_content:
                  type: string
                  description: The raw CSV content as a string
                type:
                  type: string
                  enum:
                  - TransactionImport
                  - TradeImport
                  - AccountImport
                  - MintImport
                  - CategoryImport
                  - RuleImport
                  description: Import type (defaults to TransactionImport)
                account_id:
                  type: string
                  format: uuid
                  description: Account ID to import into
                publish:
                  type: string
                  description: Set to "true" to automatically queue for processing
                    if configuration is valid
                date_col_label:
                  type: string
                  description: Header name for the date column
                amount_col_label:
                  type: string
                  description: Header name for the amount column
                name_col_label:
                  type: string
                  description: Header name for the transaction name column
                category_col_label:
                  type: string
                  description: Header name for the category column
                tags_col_label:
                  type: string
                  description: Header name for the tags column
                notes_col_label:
                  type: string
                  description: Header name for the notes column
                date_format:
                  type: string
                  description: Date format pattern (e.g., "%m/%d/%Y")
                number_format:
                  type: string
                  enum:
                  - '1,234.56'
                  - 1.234,56
                  - 1 234,56
                  - '1,234'
                  description: Number format for parsing amounts
                signage_convention:
                  type: string
                  enum:
                  - inflows_positive
                  - inflows_negative
                  description: How to interpret positive/negative amounts
                col_sep:
                  type: string
                  enum:
                  - ","
                  - ";"
                  description: Column separator
        required: true
  "/api/v1/imports/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: Import ID
      schema:
        type: string
    get:
      summary: Retrieve an import
      description: Retrieve detailed information about a specific import, including
        configuration and row statistics.
      tags:
      - Imports
      security:
      - apiKeyAuth: []
      responses:
        '200':
          description: import retrieved
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ImportResponse"
        '404':
          description: import not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/tags":
    get:
      summary: List tags
      tags:
      - Tags
      security:
      - apiKeyAuth: []
      responses:
        '200':
          description: tags listed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/TagCollection"
    post:
      summary: Create tag
      tags:
      - Tags
      security:
      - apiKeyAuth: []
      parameters: []
      responses:
        '201':
          description: tag created with auto-assigned color
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/TagDetail"
        '422':
          description: validation error - missing name
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                tag:
                  type: object
                  properties:
                    name:
                      type: string
                      description: Tag name (required)
                    color:
                      type: string
                      description: Hex color code (optional, auto-assigned if not
                        provided)
                  required:
                  - name
              required:
              - tag
        required: true
  "/api/v1/tags/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: Tag ID
      schema:
        type: string
    get:
      summary: Retrieve a tag
      tags:
      - Tags
      security:
      - apiKeyAuth: []
      responses:
        '200':
          description: tag retrieved
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/TagDetail"
        '404':
          description: tag not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
    patch:
      summary: Update a tag
      tags:
      - Tags
      security:
      - apiKeyAuth: []
      parameters: []
      responses:
        '200':
          description: tag updated
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/TagDetail"
        '404':
          description: tag not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                tag:
                  type: object
                  properties:
                    name:
                      type: string
                    color:
                      type: string
        required: true
    delete:
      summary: Delete a tag
      tags:
      - Tags
      security:
      - apiKeyAuth: []
      responses:
        '204':
          description: tag deleted
        '404':
          description: tag not found
  "/api/v1/trades":
    get:
      summary: List trades
      tags:
      - Trades
      security:
      - apiKeyAuth: []
      parameters:
      - name: page
        in: query
        required: false
        description: 'Page number (default: 1)'
        schema:
          type: integer
      - name: per_page
        in: query
        required: false
        description: 'Items per page (default: 25, max: 100)'
        schema:
          type: integer
      - name: account_id
        in: query
        required: false
        description: Filter by account ID
        schema:
          type: string
      - name: account_ids
        in: query
        required: false
        description: Filter by multiple account IDs
        schema:
          type: array
          items:
            type: string
      - name: start_date
        in: query
        required: false
        description: Filter trades from this date (inclusive)
        schema:
          type: string
          format: date
      - name: end_date
        in: query
        required: false
        description: Filter trades until this date (inclusive)
        schema:
          type: string
          format: date
      responses:
        '200':
          description: trades paginated
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/TradeCollection"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: invalid date filter
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
    post:
      summary: Create trade
      tags:
      - Trades
      security:
      - apiKeyAuth: []
      parameters: []
      responses:
        '201':
          description: trade created
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Trade"
        '422':
          description: validation error - missing security identifier
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: account not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                trade:
                  type: object
                  properties:
                    account_id:
                      type: string
                      format: uuid
                      description: Account ID (required)
                    date:
                      type: string
                      format: date
                      description: Trade date (required)
                    qty:
                      type: number
                      description: Quantity (required)
                    price:
                      type: number
                      description: Price (required)
                    type:
                      type: string
                      enum:
                      - buy
                      - sell
                      description: Trade type (required)
                    security_id:
                      type: string
                      format: uuid
                      description: Security ID (one of security_id, ticker, manual_ticker
                        required)
                    ticker:
                      type: string
                      description: Ticker symbol
                    manual_ticker:
                      type: string
                      description: Manual ticker for offline securities
                    currency:
                      type: string
                      description: Currency (defaults to account currency)
                    investment_activity_label:
                      type: string
                      description: Activity label (e.g. Buy, Sell)
                    category_id:
                      type: string
                      format: uuid
                      description: Category ID
                  required:
                  - account_id
                  - date
                  - qty
                  - price
                  - type
              required:
              - trade
        required: true
  "/api/v1/trades/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: Trade ID
      schema:
        type: string
    get:
      summary: Retrieve trade
      tags:
      - Trades
      security:
      - apiKeyAuth: []
      responses:
        '200':
          description: trade retrieved
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Trade"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: trade not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
    patch:
      summary: Update trade
      tags:
      - Trades
      security:
      - apiKeyAuth: []
      parameters: []
      responses:
        '200':
          description: trade updated
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Trade"
        '404':
          description: trade not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                trade:
                  type: object
                  properties:
                    date:
                      type: string
                      format: date
                    qty:
                      type: number
                    price:
                      type: number
                    type:
                      type: string
                      enum:
                      - buy
                      - sell
                    nature:
                      type: string
                      enum:
                      - inflow
                      - outflow
                    name:
                      type: string
                    notes:
                      type: string
                    currency:
                      type: string
                    investment_activity_label:
                      type: string
                    category_id:
                      type: string
                      format: uuid
        required: true
    delete:
      summary: Delete trade
      tags:
      - Trades
      security:
      - apiKeyAuth: []
      responses:
        '200':
          description: trade deleted
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/DeleteResponse"
        '404':
          description: trade not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/transactions":
    get:
      summary: List transactions
      tags:
      - Transactions
      security:
      - apiKeyAuth: []
      parameters:
      - name: page
        in: query
        required: false
        description: 'Page number (default: 1)'
        schema:
          type: integer
      - name: per_page
        in: query
        required: false
        description: 'Items per page (default: 25, max: 100)'
        schema:
          type: integer
      - name: account_id
        in: query
        required: false
        description: Filter by account ID
        schema:
          type: string
      - name: category_id
        in: query
        required: false
        description: Filter by category ID
        schema:
          type: string
      - name: merchant_id
        in: query
        required: false
        description: Filter by merchant ID
        schema:
          type: string
      - name: start_date
        in: query
        required: false
        description: Filter transactions from this date
        schema:
          type: string
          format: date
      - name: end_date
        in: query
        required: false
        description: Filter transactions until this date
        schema:
          type: string
          format: date
      - name: min_amount
        in: query
        required: false
        description: Filter by minimum amount
        schema:
          type: number
      - name: max_amount
        in: query
        required: false
        description: Filter by maximum amount
        schema:
          type: number
      - name: type
        in: query
        required: false
        description: Filter by transaction type
        schema:
          type: string
          enum:
          - income
          - expense
      - name: search
        in: query
        required: false
        description: Search by name, notes, or merchant name
        schema:
          type: string
      - name: account_ids
        in: query
        required: false
        description: Filter by multiple account IDs
        schema:
          type: array
          items:
            type: string
      - name: category_ids
        in: query
        required: false
        description: Filter by multiple category IDs
        schema:
          type: array
          items:
            type: string
      - name: merchant_ids
        in: query
        required: false
        description: Filter by multiple merchant IDs
        schema:
          type: array
          items:
            type: string
      - name: tag_ids
        in: query
        required: false
        description: Filter by tag IDs
        schema:
          type: array
          items:
            type: string
      responses:
        '200':
          description: transactions filtered by date range
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/TransactionCollection"
    post:
      summary: Create transaction
      tags:
      - Transactions
      security:
      - apiKeyAuth: []
      parameters: []
      responses:
        '201':
          description: transaction created
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Transaction"
        '422':
          description: validation error - missing required fields
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                transaction:
                  type: object
                  properties:
                    account_id:
                      type: string
                      format: uuid
                      description: Account ID (required)
                    date:
                      type: string
                      format: date
                      description: Transaction date
                    amount:
                      type: number
                      description: Transaction amount
                    name:
                      type: string
                      description: Transaction name/description
                    description:
                      type: string
                      description: Alternative to name field
                    notes:
                      type: string
                      description: Additional notes
                    currency:
                      type: string
                      description: Currency code (defaults to family currency)
                    category_id:
                      type: string
                      format: uuid
                      description: Category ID
                    merchant_id:
                      type: string
                      format: uuid
                      description: Merchant ID
                    nature:
                      type: string
                      enum:
                      - income
                      - expense
                      - inflow
                      - outflow
                      description: Transaction nature (determines sign)
                    tag_ids:
                      type: array
                      items:
                        type: string
                        format: uuid
                      description: Array of tag IDs
                  required:
                  - account_id
                  - date
                  - amount
                  - name
              required:
              - transaction
        required: true
  "/api/v1/transactions/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: Transaction ID
      schema:
        type: string
    get:
      summary: Retrieve a transaction
      tags:
      - Transactions
      security:
      - apiKeyAuth: []
      responses:
        '200':
          description: transaction retrieved
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Transaction"
        '404':
          description: transaction not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
    patch:
      summary: Update a transaction
      tags:
      - Transactions
      security:
      - apiKeyAuth: []
      parameters: []
      responses:
        '200':
          description: transaction updated
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Transaction"
        '404':
          description: transaction not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                transaction:
                  type: object
                  properties:
                    date:
                      type: string
                      format: date
                    amount:
                      type: number
                    name:
                      type: string
                    description:
                      type: string
                      description: Alternative to name field
                    notes:
                      type: string
                    currency:
                      type: string
                      description: Currency code
                    category_id:
                      type: string
                      format: uuid
                    merchant_id:
                      type: string
                      format: uuid
                    nature:
                      type: string
                      enum:
                      - income
                      - expense
                      - inflow
                      - outflow
                    tag_ids:
                      type: array
                      items:
                        type: string
                        format: uuid
                      description: Array of tag IDs to assign. Omit to preserve existing
                        tags; use [] to clear all tags.
        required: true
    delete:
      summary: Delete a transaction
      tags:
      - Transactions
      security:
      - apiKeyAuth: []
      responses:
        '200':
          description: transaction deleted
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/DeleteResponse"
        '404':
          description: transaction not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/valuations":
    post:
      summary: Create valuation
      tags:
      - Valuations
      security:
      - apiKeyAuth: []
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        description: Bearer token with write scope
      responses:
        '201':
          description: valuation created
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Valuation"
        '422':
          description: validation error - missing date
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: account not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                valuation:
                  type: object
                  properties:
                    account_id:
                      type: string
                      format: uuid
                      description: Account ID (required)
                    amount:
                      type: number
                      description: Valuation amount (required)
                    date:
                      type: string
                      format: date
                      description: Valuation date (required)
                    notes:
                      type: string
                      description: Additional notes
                  required:
                  - account_id
                  - amount
                  - date
              required:
              - valuation
        required: true
  "/api/v1/valuations/{id}":
    parameters:
    - name: Authorization
      in: header
      required: true
      schema:
        type: string
      description: Bearer token
    - name: id
      in: path
      required: true
      description: Valuation ID (entry ID)
      schema:
        type: string
    get:
      summary: Retrieve a valuation
      tags:
      - Valuations
      security:
      - apiKeyAuth: []
      responses:
        '200':
          description: valuation retrieved
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Valuation"
        '404':
          description: valuation not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
    patch:
      summary: Update a valuation
      tags:
      - Valuations
      security:
      - apiKeyAuth: []
      parameters: []
      responses:
        '200':
          description: valuation updated with amount and date
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Valuation"
        '422':
          description: validation error - only one of amount/date provided
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: valuation not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                valuation:
                  type: object
                  properties:
                    amount:
                      type: number
                      description: New valuation amount (must provide with date)
                    date:
                      type: string
                      format: date
                      description: New valuation date (must provide with amount)
                    notes:
                      type: string
                      description: Additional notes
        required: true
  "/api/v1/users/reset":
    delete:
      summary: Reset account
      tags:
      - Users
      description: Resets all financial data (accounts, categories, merchants, tags,
        etc.) for the current user's family while keeping the user account intact.
        The reset runs asynchronously in the background. Requires admin role.
      security:
      - apiKeyAuth: []
      responses:
        '200':
          description: account reset initiated
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/SuccessMessage"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: "forbidden \u2014 requires read_write scope and admin role"
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/users/me":
    delete:
      summary: Delete account
      tags:
      - Users
      description: Permanently deactivates the current user account and all associated
        data. This action cannot be undone.
      security:
      - apiKeyAuth: []
      responses:
        '200':
          description: account deleted
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/SuccessMessage"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: insufficient scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: deactivation failed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
