---
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.
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: OAuth2 Bearer token. Obtain a token from the /api/v1/auth/login or /api/v1/auth/refresh endpoints. The token must have the 'read' or 'read_write' scope.
  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
    FamilyExportFile:
      type: object
      required:
      - attached
      properties:
        attached:
          type: boolean
        byte_size:
          type: integer
          nullable: true
          minimum: 0
        content_type:
          type: string
          nullable: true
    FamilyExport:
      type: object
      required:
      - id
      - status
      - filename
      - downloadable
      - file
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        status:
          type: string
          enum:
          - pending
          - processing
          - completed
          - failed
        filename:
          type: string
        downloadable:
          type: boolean
        download_path:
          type: string
          nullable: true
        file:
          "$ref": "#/components/schemas/FamilyExportFile"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    FamilyExportResponse:
      type: object
      required:
      - data
      properties:
        data:
          "$ref": "#/components/schemas/FamilyExport"
    FamilyExportCollection:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          type: array
          maxItems: 100
          items:
            "$ref": "#/components/schemas/FamilyExport"
        meta:
          "$ref": "#/components/schemas/Pagination"
    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.)
    ErrorResponseWithImportId:
      type: object
      required:
      - error
      - import_id
      properties:
        error:
          type: string
        message:
          type: string
          nullable: true
        import_id:
          type: string
          format: uuid
          description: Import ID preserved for retry or inspection after upload succeeds
            but publish fails
    MfaRequiredResponse:
      type: object
      required:
      - error
      - mfa_required
      properties:
        error:
          type: string
        mfa_required:
          type: boolean
    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
          nullable: true
        status:
          type: string
    AccountDetail:
      type: object
      required:
      - id
      - name
      - balance
      - balance_cents
      - cash_balance
      - cash_balance_cents
      - currency
      - classification
      - account_type
      - status
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        balance:
          type: string
        balance_cents:
          type: integer
          description: Signed balance in minor currency units
        cash_balance:
          type: string
        cash_balance_cents:
          type: integer
          description: Signed cash balance in minor currency units
        currency:
          type: string
        classification:
          type: string
        account_type:
          type: string
          nullable: true
        subtype:
          type: string
          nullable: true
        status:
          type: string
          enum:
          - active
          - draft
          - disabled
          - pending_deletion
        institution_name:
          type: string
          nullable: true
        institution_domain:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    AccountCollection:
      type: object
      required:
      - accounts
      - pagination
      properties:
        accounts:
          type: array
          items:
            "$ref": "#/components/schemas/AccountDetail"
        pagination:
          "$ref": "#/components/schemas/Pagination"
    FamilySettings:
      type: object
      required:
      - id
      - currency
      - locale
      - date_format
      - month_start_day
      - moniker
      - default_account_sharing
      - custom_enabled_currencies
      - enabled_currencies
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
          nullable: true
        currency:
          type: string
        locale:
          type: string
        date_format:
          type: string
        country:
          type: string
          nullable: true
        timezone:
          type: string
          nullable: true
        month_start_day:
          type: integer
          minimum: 1
          maximum: 28
        moniker:
          type: string
          enum:
          - Family
          - Group
        default_account_sharing:
          type: string
          enum:
          - shared
          - private
        custom_enabled_currencies:
          type: boolean
        enabled_currencies:
          type: array
          items:
            type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    BudgetSummary:
      type: object
      required:
      - id
      - start_date
      - end_date
      - name
      - currency
      - initialized
      - current
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        start_date:
          type: string
          format: date
        end_date:
          type: string
          format: date
        name:
          type: string
        currency:
          type: string
        initialized:
          type: boolean
        current:
          type: boolean
        budgeted_spending:
          type: string
          nullable: true
        budgeted_spending_cents:
          type: integer
          nullable: true
        expected_income:
          type: string
          nullable: true
        expected_income_cents:
          type: integer
          nullable: true
        allocated_spending:
          type: string
        allocated_spending_cents:
          type: integer
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    Budget:
      type: object
      required:
      - id
      - start_date
      - end_date
      - name
      - currency
      - initialized
      - current
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        start_date:
          type: string
          format: date
        end_date:
          type: string
          format: date
        name:
          type: string
        currency:
          type: string
        initialized:
          type: boolean
        current:
          type: boolean
        budgeted_spending:
          type: string
          nullable: true
        budgeted_spending_cents:
          type: integer
          nullable: true
        expected_income:
          type: string
          nullable: true
        expected_income_cents:
          type: integer
          nullable: true
        allocated_spending:
          type: string
        allocated_spending_cents:
          type: integer
        actual_spending:
          type: string
        actual_spending_cents:
          type: integer
        actual_income:
          type: string
        actual_income_cents:
          type: integer
        available_to_spend:
          type: string
        available_to_spend_cents:
          type: integer
        available_to_allocate:
          type: string
        available_to_allocate_cents:
          type: integer
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    BudgetCollection:
      type: object
      required:
      - budgets
      - pagination
      properties:
        budgets:
          type: array
          items:
            "$ref": "#/components/schemas/BudgetSummary"
        pagination:
          "$ref": "#/components/schemas/Pagination"
    BudgetCategorySummary:
      type: object
      required:
      - id
      - budget_id
      - currency
      - subcategory
      - inherits_parent_budget
      - category
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        budget_id:
          type: string
          format: uuid
        currency:
          type: string
        subcategory:
          type: boolean
        inherits_parent_budget:
          type: boolean
        rollover_enabled:
          type: boolean
        budgeted_spending:
          type: string
        budgeted_spending_cents:
          type: integer
        display_budgeted_spending:
          type: string
        display_budgeted_spending_cents:
          type: integer
        category:
          type: object
          required:
          - id
          - name
          - color
          - lucide_icon
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
            color:
              type: string
            lucide_icon:
              type: string
            parent_id:
              type: string
              format: uuid
              nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    BudgetCategory:
      type: object
      required:
      - id
      - budget_id
      - currency
      - subcategory
      - inherits_parent_budget
      - category
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        budget_id:
          type: string
          format: uuid
        currency:
          type: string
        subcategory:
          type: boolean
        inherits_parent_budget:
          type: boolean
        rollover_enabled:
          type: boolean
        budgeted_spending:
          type: string
        budgeted_spending_cents:
          type: integer
        display_budgeted_spending:
          type: string
        display_budgeted_spending_cents:
          type: integer
        rolled_over_amount:
          type: string
        rolled_over_amount_cents:
          type: integer
        actual_spending:
          type: string
        actual_spending_cents:
          type: integer
        available_to_spend:
          type: string
        available_to_spend_cents:
          type: integer
        category:
          type: object
          required:
          - id
          - name
          - color
          - lucide_icon
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
            color:
              type: string
            lucide_icon:
              type: string
            parent_id:
              type: string
              format: uuid
              nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    BudgetCategoryCollection:
      type: object
      required:
      - budget_categories
      - pagination
      properties:
        budget_categories:
          type: array
          items:
            "$ref": "#/components/schemas/BudgetCategorySummary"
        pagination:
          "$ref": "#/components/schemas/Pagination"
    Balance:
      type: object
      required:
      - id
      - date
      - currency
      - flows_factor
      - balance
      - balance_cents
      - start_balance
      - start_balance_cents
      - end_balance
      - end_balance_cents
      - account
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        date:
          type: string
          format: date
        currency:
          type: string
        flows_factor:
          type: number
          format: float
        balance:
          type: string
        balance_cents:
          type: integer
          description: Balance in currency minor units
        cash_balance:
          type: string
          nullable: true
        cash_balance_cents:
          type: integer
          nullable: true
          description: Cash balance in currency minor units
        start_cash_balance:
          type: string
        start_cash_balance_cents:
          type: integer
          description: Starting cash balance in currency minor units
        start_non_cash_balance:
          type: string
        start_non_cash_balance_cents:
          type: integer
          description: Starting non-cash balance in currency minor units
        start_balance:
          type: string
        start_balance_cents:
          type: integer
          description: Starting total balance in currency minor units
        cash_inflows:
          type: string
        cash_inflows_cents:
          type: integer
          description: Cash inflows in currency minor units
        cash_outflows:
          type: string
        cash_outflows_cents:
          type: integer
          description: Cash outflows in currency minor units
        non_cash_inflows:
          type: string
        non_cash_inflows_cents:
          type: integer
          description: Non-cash inflows in currency minor units
        non_cash_outflows:
          type: string
        non_cash_outflows_cents:
          type: integer
          description: Non-cash outflows in currency minor units
        net_market_flows:
          type: string
        net_market_flows_cents:
          type: integer
          description: Net market flows in currency minor units
        cash_adjustments:
          type: string
        cash_adjustments_cents:
          type: integer
          description: Cash adjustments in currency minor units
        non_cash_adjustments:
          type: string
        non_cash_adjustments_cents:
          type: integer
          description: Non-cash adjustments in currency minor units
        end_cash_balance:
          type: string
        end_cash_balance_cents:
          type: integer
          description: Ending cash balance in currency minor units
        end_non_cash_balance:
          type: string
        end_non_cash_balance_cents:
          type: integer
          description: Ending non-cash balance in currency minor units
        end_balance:
          type: string
        end_balance_cents:
          type: integer
          description: Ending total balance in currency minor units
        account:
          "$ref": "#/components/schemas/BalanceAccount"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    BalanceAccount:
      type: object
      required:
      - id
      - name
      - account_type
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        account_type:
          type: string
          nullable: true
    BalanceCollection:
      type: object
      required:
      - balances
      - pagination
      properties:
        balances:
          type: array
          items:
            "$ref": "#/components/schemas/Balance"
        pagination:
          "$ref": "#/components/schemas/Pagination"
    Category:
      type: object
      required:
      - id
      - name
      - color
      - icon
      properties:
        id:
          type: string
          format: uuid
        name:
          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
      - color
      - icon
      - subcategories_count
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        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"
    CategoryCreateRequest:
      type: object
      required:
      - category
      properties:
        category:
          type: object
          required:
          - name
          properties:
            name:
              type: string
              description: Category name (required, unique within family)
            color:
              type: string
              description: 'Hex color code (e.g. #22c55e). Defaults to #6172F3 if
                omitted; subcategories inherit parent color.'
            icon:
              type: string
              description: Lucide icon name (e.g. "coffee"). Auto-suggested from the
                name when omitted.
            parent_id:
              type: string
              format: uuid
              nullable: true
              description: Parent category ID. Must belong to the same family. Categories
                support up to 2 levels of nesting.
    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
    MerchantImportResult:
      type: object
      required:
      - imported
      - skipped
      - merchants
      properties:
        imported:
          type: integer
          description: Number of merchants successfully created
        skipped:
          type: integer
          description: Number of rows skipped (duplicates or invalid)
        merchants:
          type: array
          items:
            "$ref": "#/components/schemas/MerchantDetail"
    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"
    RuleAction:
      type: object
      required:
      - id
      - action_type
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        action_type:
          type: string
        value:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    RuleCondition:
      type: object
      required:
      - id
      - condition_type
      - operator
      - sub_conditions
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        condition_type:
          type: string
        operator:
          type: string
        value:
          type: string
          nullable: true
        sub_conditions:
          type: array
          items:
            "$ref": "#/components/schemas/RuleCondition"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    Rule:
      type: object
      required:
      - id
      - resource_type
      - active
      - conditions
      - actions
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
          nullable: true
        resource_type:
          type: string
          enum:
          - transaction
        active:
          type: boolean
        effective_date:
          type: string
          format: date
          nullable: true
        conditions:
          type: array
          items:
            "$ref": "#/components/schemas/RuleCondition"
        actions:
          type: array
          items:
            "$ref": "#/components/schemas/RuleAction"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    RuleResponse:
      type: object
      required:
      - data
      properties:
        data:
          "$ref": "#/components/schemas/Rule"
    RuleCollection:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/Rule"
        meta:
          type: object
          required:
          - current_page
          - total_pages
          - total_count
          - per_page
          properties:
            current_page:
              type: integer
            next_page:
              type: integer
              nullable: true
            prev_page:
              type: integer
              nullable: true
            total_pages:
              type: integer
            total_count:
              type: integer
            per_page:
              type: integer
    RuleRun:
      type: object
      required:
      - id
      - rule_id
      - rule_name
      - execution_type
      - status
      - transactions_queued
      - transactions_processed
      - transactions_modified
      - pending_jobs_count
      - executed_at
      - rule
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        rule_id:
          type: string
          format: uuid
        rule_name:
          type: string
          nullable: true
        execution_type:
          type: string
          enum:
          - manual
          - scheduled
        status:
          type: string
          enum:
          - pending
          - success
          - failed
        transactions_queued:
          type: integer
          minimum: 0
        transactions_processed:
          type: integer
          minimum: 0
        transactions_modified:
          type: integer
          minimum: 0
        pending_jobs_count:
          type: integer
          minimum: 0
        executed_at:
          type: string
          format: date-time
        error_message:
          type: string
          nullable: true
        rule:
          type: object
          nullable: true
          required:
          - id
          - resource_type
          - active
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
              nullable: true
            resource_type:
              type: string
            active:
              type: boolean
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    RuleRunResponse:
      type: object
      required:
      - data
      properties:
        data:
          "$ref": "#/components/schemas/RuleRun"
    RuleRunCollection:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/RuleRun"
        meta:
          type: object
          required:
          - current_page
          - total_pages
          - total_count
          - per_page
          properties:
            current_page:
              type: integer
            next_page:
              type: integer
              nullable: true
            prev_page:
              type: integer
              nullable: true
            total_pages:
              type: integer
            total_count:
              type: integer
            per_page:
              type: integer
    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
    RecurringTransaction:
      type: object
      required:
      - id
      - amount
      - amount_cents
      - currency
      - expected_day_of_month
      - last_occurrence_date
      - next_expected_date
      - status
      - occurrence_count
      - manual
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        amount:
          type: string
        amount_cents:
          type: integer
          description: Amount in currency minor units
        currency:
          type: string
        expected_day_of_month:
          type: integer
          minimum: 1
          maximum: 31
        last_occurrence_date:
          type: string
          format: date
        next_expected_date:
          type: string
          format: date
        status:
          type: string
          enum:
          - active
          - inactive
        occurrence_count:
          type: integer
          minimum: 0
        name:
          type: string
          nullable: true
        manual:
          type: boolean
        expected_amount_min:
          type: string
          nullable: true
        expected_amount_min_cents:
          type: integer
          nullable: true
          description: Minimum expected amount in currency minor units
        expected_amount_max:
          type: string
          nullable: true
        expected_amount_max_cents:
          type: integer
          nullable: true
          description: Maximum expected amount in currency minor units
        expected_amount_avg:
          type: string
          nullable: true
        expected_amount_avg_cents:
          type: integer
          nullable: true
          description: Average expected amount in currency minor units
        account:
          "$ref": "#/components/schemas/Account"
          nullable: true
        merchant:
          "$ref": "#/components/schemas/Merchant"
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    RecurringTransactionCollection:
      type: object
      required:
      - recurring_transactions
      - pagination
      properties:
        recurring_transactions:
          type: array
          items:
            "$ref": "#/components/schemas/RecurringTransaction"
        pagination:
          "$ref": "#/components/schemas/Pagination"
    Transaction:
      type: object
      required:
      - id
      - date
      - amount
      - amount_cents
      - signed_amount_cents
      - currency
      - name
      - classification
      - account
      - tags
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        date:
          type: string
          format: date
        amount:
          type: string
        amount_cents:
          type: integer
          description: Absolute transaction amount in currency minor units (e.g. cents for USD). Always positive.
        signed_amount_cents:
          type: integer
          description: Signed transaction amount in currency minor units. Negative for expenses/outflows, positive for income/inflows.
        currency:
          type: string
        name:
          type: string
        notes:
          type: string
          nullable: true
        external_id:
          type: string
          nullable: true
        source:
          type: string
          nullable: true
        user_modified:
          type: boolean
          description: >
            Whether this transaction has been flagged as user-modified. When true,
            the next bank sync will not overwrite fields the API client already set
            (name, category, etc.), matching the protection applied to manual edits.
        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"
    TransferTransactionSide:
      type: object
      required:
      - id
      - entry_id
      - date
      - amount
      - amount_cents
      - currency
      - name
      - kind
      - account
      properties:
        id:
          type: string
          format: uuid
        entry_id:
          type: string
          format: uuid
        date:
          type: string
          format: date
        amount:
          type: string
        amount_cents:
          type: integer
          description: Signed amount in currency minor units
        currency:
          type: string
        name:
          type: string
        kind:
          type: string
        account:
          type: object
          required:
          - id
          - name
          - account_type
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
            account_type:
              type: string
              nullable: true
    TransferDecision:
      type: object
      required:
      - id
      - status
      - date
      - amount
      - amount_cents
      - currency
      - transfer_type
      - inflow_transaction
      - outflow_transaction
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        status:
          type: string
          enum:
          - pending
          - confirmed
        date:
          type: string
          format: date
        amount:
          type: string
        amount_cents:
          type: integer
          description: Absolute transfer amount in currency minor units
        currency:
          type: string
        transfer_type:
          type: string
          enum:
          - transfer
          - liability_payment
          - loan_payment
        notes:
          type: string
          nullable: true
        source_fee_amount:
          type: string
          nullable: true
          description: Fee charged to the source account
        source_fee_currency:
          type: string
          nullable: true
        destination_fee_amount:
          type: string
          nullable: true
          description: Fee deducted from the destination account
        destination_fee_currency:
          type: string
          nullable: true
        inflow_transaction:
          "$ref": "#/components/schemas/TransferTransactionSide"
        outflow_transaction:
          "$ref": "#/components/schemas/TransferTransactionSide"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    TransferDecisionCollection:
      type: object
      required:
      - transfers
      - pagination
      properties:
        transfers:
          type: array
          items:
            "$ref": "#/components/schemas/TransferDecision"
        pagination:
          "$ref": "#/components/schemas/Pagination"
    RejectedTransfer:
      type: object
      required:
      - id
      - inflow_transaction
      - outflow_transaction
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        inflow_transaction:
          "$ref": "#/components/schemas/TransferTransactionSide"
        outflow_transaction:
          "$ref": "#/components/schemas/TransferTransactionSide"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    RejectedTransferCollection:
      type: object
      required:
      - rejected_transfers
      - pagination
      properties:
        rejected_transfers:
          type: array
          items:
            "$ref": "#/components/schemas/RejectedTransfer"
        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
    ValuationCollection:
      type: object
      required:
      - valuations
      - pagination
      properties:
        valuations:
          type: array
          items:
            "$ref": "#/components/schemas/Valuation"
        pagination:
          "$ref": "#/components/schemas/Pagination"
    DeleteResponse:
      type: object
      required:
      - message
      properties:
        message:
          type: string
    TransactionResponse:
      type: object
      required:
      - id
      - date
      - amount
      - currency
      - name
      - entryable_type
      - account
      properties:
        id:
          type: string
          format: uuid
        date:
          type: string
          format: date
        amount:
          type: string
        currency:
          type: string
        name:
          type: string
        entryable_type:
          type: string
        account:
          type: object
          required:
          - id
          - name
          - account_type
          properties:
            id:
              type: string
              format: uuid
            name:
              type: string
            account_type:
              type: string
              nullable: true
    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
      required:
      - rows_count
      - valid_rows_count
      - invalid_rows_count
      - mappings_count
      - unassigned_mappings_count
      properties:
        rows_count:
          type: integer
          minimum: 0
        valid_rows_count:
          type: integer
          minimum: 0
        invalid_rows_count:
          type: integer
          minimum: 0
        mappings_count:
          type: integer
          minimum: 0
        unassigned_mappings_count:
          type: integer
          minimum: 0
    ImportVerificationReadback:
      type: object
      description: SureImport only. Expected NDJSON counts compared to family-scoped
        database readback after publish.
      properties:
        status:
          type: string
          enum:
          - not_verified
          - matched
          - mismatch
          - failed
          - reverted
        checked_at:
          type: string
          format: date-time
          nullable: true
        expected_record_counts:
          type: object
          additionalProperties:
            type: integer
        before_counts:
          type: object
          additionalProperties:
            type: integer
        after_counts:
          type: object
          additionalProperties:
            type: integer
        actual_delta_counts:
          type: object
          additionalProperties:
            type: integer
        checked_counts:
          type: object
          additionalProperties:
            type: integer
        mismatches:
          type: object
          additionalProperties:
            type: object
            required:
            - expected
            - actual
            properties:
              expected:
                type: integer
              actual:
                type: integer
        error:
          type: string
          nullable: true
    ImportVerification:
      type: object
      description: SureImport only. Captured at upload and completed after import
        publish.
      required:
      - expected_record_counts
      - readback
      properties:
        expected_record_counts:
          type: object
          additionalProperties:
            type: integer
        readback:
          "$ref": "#/components/schemas/ImportVerificationReadback"
    ImportPreflightContent:
      type: object
      required:
      - filename
      - content_type
      - byte_size
      properties:
        filename:
          type: string
        content_type:
          type: string
        byte_size:
          type: integer
          minimum: 0
    ImportPreflightError:
      type: object
      required:
      - code
      - message
      properties:
        code:
          type: string
        message:
          type: string
    ImportPreflightStats:
      type: object
      required:
      - rows_count
      properties:
        rows_count:
          type: integer
          minimum: 0
          description: CSV parsed non-header rows, or nonblank Sure NDJSON lines.
        valid_rows_count:
          type: integer
          minimum: 0
          description: SureImport only. Valid NDJSON records.
        invalid_rows_count:
          type: integer
          minimum: 0
          description: SureImport only. Invalid NDJSON records. CSV malformed content
            returns a 422 instead.
        entity_counts:
          type: object
          additionalProperties:
            type: integer
          nullable: true
        record_type_counts:
          type: object
          additionalProperties:
            type: integer
          nullable: true
    ImportPreflight:
      type: object
      required:
      - type
      - valid
      - content
      - stats
      - errors
      - warnings
      properties:
        type:
          type: string
          enum:
          - TransactionImport
          - TradeImport
          - AccountImport
          - MintImport
          - ActualImport
          - YnabImport
          - CategoryImport
          - RuleImport
          - MerchantImport
          - PdfImport
          - QifImport
          - SureImport
        valid:
          type: boolean
        content:
          "$ref": "#/components/schemas/ImportPreflightContent"
        stats:
          "$ref": "#/components/schemas/ImportPreflightStats"
        headers:
          type: array
          items:
            type: string
          nullable: true
        required_headers:
          type: array
          items:
            type: string
          nullable: true
        missing_required_headers:
          type: array
          items:
            type: string
          nullable: true
        errors:
          type: array
          items:
            "$ref": "#/components/schemas/ImportPreflightError"
        warnings:
          type: array
          items:
            type: string
    ImportPreflightResponse:
      type: object
      required:
      - data
      properties:
        data:
          "$ref": "#/components/schemas/ImportPreflight"
    ImportStatusSummary:
      type: object
      required:
      - uploaded
      - configured
      - terminal
      properties:
        uploaded:
          type: boolean
        configured:
          type: boolean
        terminal:
          type: boolean
    ImportStatusDetail:
      allOf:
      - "$ref": "#/components/schemas/ImportStatusSummary"
      - type: object
        required:
        - cleaned
        - publishable
        - revertable
        properties:
          cleaned:
            type: boolean
          publishable:
            type: boolean
          revertable:
            type: boolean
    ImportSummary:
      type: object
      required:
      - id
      - type
      - status
      - created_at
      - updated_at
      - status_detail
      properties:
        id:
          type: string
          format: uuid
        type:
          type: string
          enum:
          - TransactionImport
          - TradeImport
          - AccountImport
          - MintImport
          - ActualImport
          - YnabImport
          - CategoryImport
          - RuleImport
          - MerchantImport
          - PdfImport
          - QifImport
          - SureImport
        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
        status_detail:
          "$ref": "#/components/schemas/ImportStatusSummary"
    ImportDetail:
      type: object
      required:
      - id
      - type
      - status
      - created_at
      - updated_at
      - status_detail
      - configuration
      - stats
      properties:
        id:
          type: string
          format: uuid
        type:
          type: string
          enum:
          - TransactionImport
          - TradeImport
          - AccountImport
          - MintImport
          - ActualImport
          - YnabImport
          - CategoryImport
          - RuleImport
          - MerchantImport
          - PdfImport
          - QifImport
          - SureImport
        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
        status_detail:
          "$ref": "#/components/schemas/ImportStatusDetail"
        configuration:
          "$ref": "#/components/schemas/ImportConfiguration"
        stats:
          "$ref": "#/components/schemas/ImportStats"
        verification:
          "$ref": "#/components/schemas/ImportVerification"
    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"
    ImportSessionChunk:
      type: object
      required:
      - id
      - sequence
      - status
      - rows_count
      - summary
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        sequence:
          type: integer
          minimum: 1
        client_chunk_id:
          type: string
          nullable: true
        status:
          type: string
          enum:
          - pending
          - importing
          - complete
          - failed
        rows_count:
          type: integer
          minimum: 0
        summary:
          type: object
          additionalProperties:
            type: object
            additionalProperties:
              type: integer
        error:
          type: object
          nullable: true
          additionalProperties: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    ImportSession:
      type: object
      required:
      - id
      - type
      - status
      - chunks_count
      - summary
      - chunks
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        type:
          type: string
          enum:
          - SureImport
        status:
          type: string
          enum:
          - pending
          - importing
          - complete
          - failed
        client_session_id:
          type: string
          nullable: true
        expected_chunks:
          type: integer
          nullable: true
          minimum: 1
        chunks_count:
          type: integer
          minimum: 0
        summary:
          type: object
          additionalProperties:
            type: object
            additionalProperties:
              type: integer
        error:
          type: object
          nullable: true
          additionalProperties: true
        chunks:
          type: array
          items:
            "$ref": "#/components/schemas/ImportSessionChunk"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    ImportSessionResponse:
      type: object
      required:
      - data
      properties:
        data:
          "$ref": "#/components/schemas/ImportSession"
    ProviderConnectionInstitution:
      type: object
      required:
      - name
      properties:
        name:
          type: string
          nullable: true
        domain:
          type: string
          nullable: true
        url:
          type: string
          nullable: true
    ProviderConnectionAccounts:
      type: object
      required:
      - total_count
      - linked_count
      - unlinked_count
      properties:
        total_count:
          type: integer
          minimum: 0
        linked_count:
          type: integer
          minimum: 0
        unlinked_count:
          type: integer
          minimum: 0
    ProviderConnectionSyncLatest:
      type: object
      required:
      - id
      - status
      - created_at
      properties:
        id:
          type: string
          format: uuid
        status:
          type: string
        created_at:
          type: string
          format: date-time
        syncing_at:
          type: string
          format: date-time
          nullable: true
        completed_at:
          type: string
          format: date-time
          nullable: true
        failed_at:
          type: string
          format: date-time
          nullable: true
        error:
          type: object
          nullable: true
          description: Sanitized latest sync error summary. Null when the latest sync
            is not failed or stale.
          required:
          - present
          properties:
            present:
              type: boolean
              description: Always true when this object is present.
            message:
              type: string
              nullable: true
              description: Stable sanitized error category message; raw provider error
                text is never exposed.
    ProviderConnectionSync:
      type: object
      required:
      - syncing
      properties:
        syncing:
          type: boolean
        status_summary:
          type: string
          nullable: true
        last_synced_at:
          type: string
          format: date-time
          nullable: true
        latest:
          allOf:
          - "$ref": "#/components/schemas/ProviderConnectionSyncLatest"
          nullable: true
    ProviderConnection:
      type: object
      required:
      - id
      - provider
      - provider_type
      - name
      - status
      - requires_update
      - credentials_configured
      - scheduled_for_deletion
      - pending_account_setup
      - institution
      - accounts
      - sync
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        provider:
          type: string
        provider_type:
          type: string
        name:
          type: string
        status:
          type: string
          nullable: true
        requires_update:
          type: boolean
          nullable: true
          description: False when the provider item does not expose this status.
        credentials_configured:
          type: boolean
          nullable: true
          description: False when credential readiness is unknown.
        scheduled_for_deletion:
          type: boolean
          nullable: true
          description: False when the provider item does not expose this status.
        pending_account_setup:
          type: boolean
          nullable: true
          description: False when account setup state is unknown.
        institution:
          "$ref": "#/components/schemas/ProviderConnectionInstitution"
        accounts:
          "$ref": "#/components/schemas/ProviderConnectionAccounts"
        sync:
          "$ref": "#/components/schemas/ProviderConnectionSync"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    ProviderConnectionCollection:
      type: object
      required:
      - data
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/ProviderConnection"
    ImportRowMapping:
      type: object
      required:
      - key
      - type
      - value
      - create_when_empty
      - creatable
      - mappable
      properties:
        key:
          type: string
          nullable: true
        type:
          type: string
        value:
          type: string
          nullable: true
        create_when_empty:
          type: boolean
        creatable:
          type: boolean
        mappable:
          type: object
          nullable: true
          properties:
            id:
              type: string
              format: uuid
            type:
              type: string
            name:
              type: string
              nullable: true
    ImportRowDiagnostic:
      type: object
      required:
      - id
      - row_number
      - valid
      - errors
      - fields
      - mappings
      properties:
        id:
          type: string
          format: uuid
        row_number:
          type: integer
          minimum: 1
        valid:
          type: boolean
        errors:
          type: array
          items:
            type: string
        fields:
          type: object
          properties:
            account:
              type: string
              nullable: true
            date:
              type: string
              nullable: true
            qty:
              type: string
              nullable: true
            ticker:
              type: string
              nullable: true
            exchange_operating_mic:
              type: string
              nullable: true
            price:
              type: string
              nullable: true
            amount:
              type: string
              nullable: true
            currency:
              type: string
              nullable: true
            name:
              type: string
              nullable: true
            category:
              type: string
              nullable: true
            tags:
              type: string
              nullable: true
            entity_type:
              type: string
              nullable: true
            notes:
              type: string
              nullable: true
            active:
              type: boolean
              nullable: true
            effective_date:
              type: string
              nullable: true
            conditions:
              type: string
              nullable: true
            actions:
              type: string
              nullable: true
        mappings:
          type: object
          properties:
            account:
              "$ref": "#/components/schemas/ImportRowMapping"
            category:
              "$ref": "#/components/schemas/ImportRowMapping"
            account_type:
              "$ref": "#/components/schemas/ImportRowMapping"
            tags:
              type: array
              items:
                "$ref": "#/components/schemas/ImportRowMapping"
    ImportRowDiagnosticCollection:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          type: array
          items:
            "$ref": "#/components/schemas/ImportRowDiagnostic"
        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
    SyncableSummary:
      type: object
      required:
      - type
      - id
      properties:
        type:
          type: string
        id:
          type: string
          format: uuid
        name:
          type: string
          nullable: true
    SyncErrorSummary:
      type: object
      required:
      - message
      properties:
        message:
          type: string
    SyncResource:
      type: object
      required:
      - id
      - status
      - in_progress
      - terminal
      - syncable
      - children_count
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        status:
          type: string
          enum:
          - pending
          - syncing
          - completed
          - failed
          - stale
        in_progress:
          type: boolean
        terminal:
          type: boolean
        syncable:
          "$ref": "#/components/schemas/SyncableSummary"
        parent_id:
          type: string
          format: uuid
          nullable: true
        children_count:
          type: integer
          minimum: 0
        window_start_date:
          type: string
          format: date
          nullable: true
        window_end_date:
          type: string
          format: date
          nullable: true
        pending_at:
          type: string
          format: date-time
          nullable: true
        syncing_at:
          type: string
          format: date-time
          nullable: true
        completed_at:
          type: string
          format: date-time
          nullable: true
        failed_at:
          type: string
          format: date-time
          nullable: true
        error:
          nullable: true
          allOf:
          - "$ref": "#/components/schemas/SyncErrorSummary"
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    SyncResponse:
      type: object
      required:
      - data
      properties:
        data:
          nullable: true
          allOf:
          - "$ref": "#/components/schemas/SyncResource"
    SyncCollection:
      type: object
      required:
      - data
      - meta
      properties:
        data:
          type: array
          maxItems: 100
          items:
            "$ref": "#/components/schemas/SyncResource"
        meta:
          "$ref": "#/components/schemas/Pagination"
    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"
    Security:
      type: object
      required:
      - id
      - ticker
      - kind
      - offline
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        ticker:
          type: string
        name:
          type: string
          nullable: true
        kind:
          type: string
          enum:
          - standard
          - cash
        country_code:
          type: string
          nullable: true
        exchange_mic:
          type: string
          nullable: true
        exchange_acronym:
          type: string
          nullable: true
        exchange_operating_mic:
          type: string
          nullable: true
        exchange_name:
          type: string
          nullable: true
        offline:
          type: boolean
        offline_reason:
          type: string
          nullable: true
        website_url:
          type: string
          nullable: true
        logo_url:
          type: string
          nullable: true
        first_provider_price_on:
          type: string
          format: date
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    SecurityCollection:
      type: object
      required:
      - securities
      - pagination
      properties:
        securities:
          type: array
          items:
            "$ref": "#/components/schemas/Security"
        pagination:
          "$ref": "#/components/schemas/Pagination"
    SecurityPrice:
      type: object
      required:
      - id
      - date
      - price
      - price_amount
      - currency
      - provisional
      - security
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        date:
          type: string
          format: date
        price:
          type: string
          description: Formatted security price
        price_amount:
          type: string
          description: Exact decimal security price
        currency:
          type: string
        provisional:
          type: boolean
        security:
          type: object
          required:
          - id
          - ticker
          properties:
            id:
              type: string
              format: uuid
            ticker:
              type: string
            name:
              type: string
              nullable: true
            exchange_operating_mic:
              type: string
              nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    SecurityPriceCollection:
      type: object
      required:
      - security_prices
      - pagination
      properties:
        security_prices:
          type: array
          items:
            "$ref": "#/components/schemas/SecurityPrice"
        pagination:
          "$ref": "#/components/schemas/Pagination"
    Money:
      type: object
      required:
      - amount
      - currency
      - formatted
      properties:
        amount:
          type: string
          description: Numeric amount as string
        currency:
          type: string
          description: ISO 4217 currency code
        formatted:
          type: string
          description: Locale-formatted money string
    BalanceSheet:
      type: object
      required:
      - currency
      - net_worth
      - assets
      - liabilities
      properties:
        currency:
          type: string
          description: Family primary currency
        net_worth:
          "$ref": "#/components/schemas/Money"
        assets:
          "$ref": "#/components/schemas/Money"
        liabilities:
          "$ref": "#/components/schemas/Money"
    SuccessMessage:
      type: object
      required:
      - message
      properties:
        message:
          type: string
    ResetInitiatedResponse:
      type: object
      required:
      - message
      - status
      - job_id
      - family_id
      - status_url
      properties:
        message:
          type: string
        status:
          type: string
          enum:
          - queued
        job_id:
          type: string
          description: Informational Active Job identifier returned by the queue adapter;
            reset status is family-scoped, not job-scoped.
        family_id:
          type: string
          format: uuid
          description: UUID of the family being reset.
        status_url:
          type: string
    ResetStatusResponse:
      type: object
      required:
      - status
      - family_id
      - reset_complete
      - counts
      properties:
        status:
          type: string
          enum:
          - complete
          - data_remaining
          description: Counts-based family reset status at response time.
        family_id:
          type: string
          format: uuid
          description: UUID of the family whose reset target counts were checked.
        reset_complete:
          type: boolean
          description: True when all reset target counts are zero at response time.
            This is a family data snapshot, not a durable per-job completion record.
        counts:
          type: object
          required:
          - account_statements
          - family_exports
          - imports
          - import_sessions
          - import_source_mappings
          - import_rows
          - import_mappings
          - accounts
          - account_shares
          - account_providers
          - entries
          - transactions
          - transfers
          - rejected_transfers
          - valuations
          - trades
          - holdings
          - balances
          - recurring_transactions
          - rules
          - rule_actions
          - rule_conditions
          - rule_runs
          - budgets
          - budget_categories
          - categories
          - tags
          - taggings
          - merchants
          - family_merchant_associations
          - provider_items
          - active_storage_attachments
          - plaid_items
          additionalProperties:
            type: integer
            minimum: 0
          properties:
            account_statements:
              type: integer
              minimum: 0
            family_exports:
              type: integer
              minimum: 0
            imports:
              type: integer
              minimum: 0
            import_sessions:
              type: integer
              minimum: 0
            import_source_mappings:
              type: integer
              minimum: 0
            import_rows:
              type: integer
              minimum: 0
            import_mappings:
              type: integer
              minimum: 0
            accounts:
              type: integer
              minimum: 0
            account_shares:
              type: integer
              minimum: 0
            account_providers:
              type: integer
              minimum: 0
            entries:
              type: integer
              minimum: 0
            transactions:
              type: integer
              minimum: 0
            transfers:
              type: integer
              minimum: 0
            rejected_transfers:
              type: integer
              minimum: 0
            valuations:
              type: integer
              minimum: 0
            trades:
              type: integer
              minimum: 0
            holdings:
              type: integer
              minimum: 0
            balances:
              type: integer
              minimum: 0
            recurring_transactions:
              type: integer
              minimum: 0
            rules:
              type: integer
              minimum: 0
            rule_actions:
              type: integer
              minimum: 0
            rule_conditions:
              type: integer
              minimum: 0
            rule_runs:
              type: integer
              minimum: 0
            budgets:
              type: integer
              minimum: 0
            budget_categories:
              type: integer
              minimum: 0
            categories:
              type: integer
              minimum: 0
            tags:
              type: integer
              minimum: 0
            taggings:
              type: integer
              minimum: 0
            merchants:
              type: integer
              minimum: 0
            family_merchant_associations:
              type: integer
              minimum: 0
            provider_items:
              type: integer
              minimum: 0
            active_storage_attachments:
              type: integer
              minimum: 0
            plaid_items:
              type: integer
              minimum: 0
    Insight:
      type: object
      required:
      - id
      - type
      - title
      - body
      - priority
      - status
      - generated_at
      properties:
        id:
          type: string
          format: uuid
        type:
          type: string
          enum:
          - spending_anomaly
          - cash_flow_warning
          - net_worth_milestone
          - subscription_audit
          - savings_rate_change
          - idle_cash
          - budget_at_risk
          - budget_on_track
          description: The insight type that describes the financial observation.
        title:
          type: string
          description: Short summary of the insight.
        body:
          type: string
          description: Full prose explanation of the insight, written by the LLM from pre-computed numbers.
        priority:
          type: string
          enum:
          - high
          - medium
          - low
        status:
          type: string
          enum:
          - active
          - read
          - acknowledged
          - expired
          description: >
            User-facing status of the insight. `active` and `read` are visible in the feed;
            `acknowledged` means the user dismissed it; `expired` means the underlying
            condition cleared.
        generated_at:
          type: string
          format: date-time
          nullable: true
    InsightCollection:
      type: object
      required:
      - insights
      properties:
        insights:
          type: array
          items:
            "$ref": "#/components/schemas/Insight"
    PushSubscription:
      type: object
      required:
      - id
      - environment
      - platform
      - last_registered_at
      properties:
        id:
          type: string
          format: uuid
        environment:
          type: string
          enum:
          - sandbox
          - production
        platform:
          type: string
          enum:
          - ios
        last_registered_at:
          type: string
          format: date-time
          description: When the device last registered this token.
paths:
  "/api/v1/accounts":
    get:
      summary: List accounts
      tags:
      - Accounts
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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: include_disabled
        in: query
        required: false
        description: Include disabled accounts in the response. Defaults to false.
        schema:
          type: boolean
      responses:
        '200':
          description: accounts paginated
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/AccountCollection"
  "/api/v1/accounts/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: Account ID
      schema:
        type: string
        format: uuid
    get:
      summary: Retrieve an account
      tags:
      - Accounts
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      parameters:
      - name: include_disabled
        in: query
        required: false
        description: Allow retrieving a disabled account. Defaults to false.
        schema:
          type: boolean
      responses:
        '200':
          description: account retrieved
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/AccountDetail"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: insufficient scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: account not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/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/sso_link":
    post:
      summary: Link an existing account via SSO
      tags:
      - Auth
      description: Authenticates with email/password and links the SSO identity from
        a previously issued linking code. Creates an OidcIdentity, logs the link via
        SsoAuditLog, and issues mobile OAuth tokens.
      parameters: []
      responses:
        '200':
          description: account linked and 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
        '400':
          description: missing linking code
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '401':
          description: invalid credentials or expired linking code
          content:
            application/json:
              schema:
                oneOf:
                - "$ref": "#/components/schemas/ErrorResponse"
                - "$ref": "#/components/schemas/MfaRequiredResponse"
        '403':
          description: SSO identity removed by an administrator
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                linking_code:
                  type: string
                  description: One-time linking code from mobile SSO onboarding redirect
                email:
                  type: string
                  format: email
                  description: Email of the existing account to link
                password:
                  type: string
                  description: Password for the existing account
              required:
              - linking_code
              - email
              - password
        required: true
  "/api/v1/auth/sso_create_account":
    post:
      summary: Create a new account via SSO
      tags:
      - Auth
      description: Creates a new user and family from a previously issued linking
        code. Links the SSO identity via OidcIdentity, logs the JIT account creation
        via SsoAuditLog, and issues mobile OAuth tokens. The linking code must have
        allow_account_creation enabled.
      parameters: []
      responses:
        '200':
          description: account created and 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
        '400':
          description: missing linking code
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '401':
          description: invalid or expired linking code
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: SSO identity removed or account creation disabled
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: user validation error
          content:
            application/json:
              schema:
                type: object
                properties:
                  errors:
                    type: array
                    items:
                      type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                linking_code:
                  type: string
                  description: One-time linking code from mobile SSO onboarding redirect
                first_name:
                  type: string
                  description: First name (overrides value from SSO provider if provided)
                last_name:
                  type: string
                  description: Last name (overrides value from SSO provider if provided)
              required:
              - linking_code
        required: true
  "/api/v1/auth/enable_ai":
    patch:
      summary: Enable AI features for the authenticated user
      tags:
      - Auth
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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"
        '403':
          description: insufficient scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/balance_sheet":
    get:
      summary: Show balance sheet
      tags:
      - Balance Sheet
      description: Returns the family balance sheet including net worth, total assets,
        and total liabilities with amounts converted to the family's primary currency.
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: balance sheet returned
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/BalanceSheet"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/balances":
    get:
      summary: List balance history records
      tags:
      - Balances
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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
          format: uuid
      - name: currency
        in: query
        required: false
        description: Filter by currency code
        schema:
          type: string
      - name: start_date
        in: query
        required: false
        description: Filter balances from this date
        schema:
          type: string
          format: date
      - name: end_date
        in: query
        required: false
        description: Filter balances until this date
        schema:
          type: string
          format: date
      responses:
        '200':
          description: balances listed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/BalanceCollection"
        '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: invalid filter
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/balances/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: Balance ID
      schema:
        type: string
        format: uuid
    get:
      summary: Retrieve a balance history record
      tags:
      - Balances
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: balance retrieved
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Balance"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: insufficient scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: balance not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/budget_categories":
    get:
      summary: List budget categories
      tags:
      - Budget Categories
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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: budget_id
        in: query
        required: false
        schema:
          type: string
          format: uuid
        description: Filter by budget ID
      - name: category_id
        in: query
        required: false
        schema:
          type: string
          format: uuid
        description: Filter by category ID
      - name: start_date
        in: query
        required: false
        schema:
          type: string
          format: date
        description: Filter budget categories whose budget starts on or after this
          date
      - name: end_date
        in: query
        required: false
        schema:
          type: string
          format: date
        description: Filter budget categories whose budget ends on or before this
          date
      responses:
        '200':
          description: budget categories listed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/BudgetCategoryCollection"
        '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: invalid filter
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/budget_categories/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: Budget category ID
      schema:
        type: string
        format: uuid
    get:
      summary: Retrieve a budget category
      tags:
      - Budget Categories
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: budget category retrieved
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/BudgetCategory"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: insufficient scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: budget category not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/budgets":
    get:
      summary: List budgets
      tags:
      - Budgets
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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: start_date
        in: query
        required: false
        schema:
          type: string
          format: date
        description: Filter budgets starting on or after this date
      - name: end_date
        in: query
        required: false
        schema:
          type: string
          format: date
        description: Filter budgets ending on or before this date
      responses:
        '200':
          description: budgets listed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/BudgetCollection"
        '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: invalid date filter
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/budgets/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: Budget ID
      schema:
        type: string
        format: uuid
    get:
      summary: Retrieve a budget
      tags:
      - Budgets
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: budget retrieved
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Budget"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: insufficient scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: budget not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/categories":
    get:
      summary: List categories
      tags:
      - Categories
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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: 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"
    post:
      summary: Create category
      tags:
      - Categories
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      parameters: []
      responses:
        '201':
          description: subcategory created with parent
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/CategoryDetail"
        '422':
          description: validation error - duplicate name
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: forbidden - api key missing read_write scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '400':
          description: bad request - missing category payload
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '401':
          description: unauthorized - missing api key
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        content:
          application/json:
            schema:
              "$ref": "#/components/schemas/CategoryCreateRequest"
        required: true
  "/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: []
      - bearerAuth: []
      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: []
      - bearerAuth: []
      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: []
      - bearerAuth: []
      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: []
      - bearerAuth: []
      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: []
      - bearerAuth: []
      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: []
      - bearerAuth: []
      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: []
      - bearerAuth: []
      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: []
      - bearerAuth: []
      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/family_exports":
    get:
      summary: Lists family exports
      tags:
      - Family Exports
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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: family exports listed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/FamilyExportCollection"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: forbidden
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
    post:
      summary: Queues a family export
      tags:
      - Family Exports
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      parameters: []
      responses:
        '202':
          description: family export queued
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/FamilyExportResponse"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: forbidden
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: invalid params
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              description: Family export creation does not accept request parameters.
  "/api/v1/family_exports/{id}":
    parameters:
    - name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    get:
      summary: Shows a family export
      tags:
      - Family Exports
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: family export shown
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/FamilyExportResponse"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: forbidden
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/family_exports/{id}/download":
    parameters:
    - name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    get:
      summary: Downloads a completed family export
      tags:
      - Family Exports
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '302':
          description: family export download redirected
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: forbidden
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '409':
          description: export not ready
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/family_settings":
    get:
      summary: Retrieve family settings
      description: Retrieve a read-only snapshot of non-secret family configuration.
      tags:
      - Family Settings
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: family settings retrieved
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/FamilySettings"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: insufficient scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/holdings":
    get:
      summary: List holdings
      tags:
      - Holdings
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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: []
      - bearerAuth: []
      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/import_sessions":
    post:
      summary: Create import session
      description: Create or idempotently retrieve a multi-file SureImport session
        keyed by client_session_id.
      tags:
      - Import Sessions
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      parameters: []
      responses:
        '201':
          description: import session created
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ImportSessionResponse"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: insufficient scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '409':
          description: client session conflict
          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:
                type:
                  type: string
                  enum:
                  - SureImport
                  description: Import session type. Only SureImport is supported.
                client_session_id:
                  type: string
                  nullable: true
                  description: Client-provided idempotency key for the full import
                    session.
                expected_chunks:
                  type: integer
                  minimum: 1
                  nullable: true
                  description: Expected number of ordered chunks before publish is
                    allowed.
  "/api/v1/import_sessions/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: Import session ID
      schema:
        type: string
    get:
      summary: Retrieve import session
      description: Retrieve import session status, chunk status, per-entity summary
        counts, and safe error details.
      tags:
      - Import Sessions
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: import session retrieved
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ImportSessionResponse"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: insufficient scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: import session not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/import_sessions/{id}/chunks":
    parameters:
    - name: id
      in: path
      required: true
      description: Import session ID
      schema:
        type: string
    post:
      summary: Upload import session chunk
      description: Attach an ordered Sure NDJSON chunk to an import session. Chunks
        are idempotent by sequence and client_chunk_id with content verification.
      tags:
      - Import Sessions
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - sequence
              - raw_file_content
              properties:
                sequence:
                  type: integer
                  minimum: 1
                  description: One-based chunk sequence. Earlier dependency chunks
                    must have lower sequence numbers.
                client_chunk_id:
                  type: string
                  nullable: true
                  description: Client-provided idempotency key for this chunk.
                raw_file_content:
                  type: string
                  description: Raw Sure NDJSON content. Each chunk is limited to 10MB.
          multipart/form-data:
            schema:
              type: object
              required:
              - sequence
              - file
              properties:
                sequence:
                  type: integer
                  minimum: 1
                  description: One-based chunk sequence. Earlier dependency chunks
                    must have lower sequence numbers.
                client_chunk_id:
                  type: string
                  nullable: true
                  description: Client-provided idempotency key for this chunk.
                file:
                  type: string
                  format: binary
                  description: Multipart Sure NDJSON file upload. Each chunk is limited
                    to 10MB.
      parameters: []
      responses:
        '201':
          description: chunk uploaded
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ImportSessionResponse"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: insufficient scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '409':
          description: chunk conflict
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: import session not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: missing or invalid content
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/import_sessions/{id}/publish":
    parameters:
    - name: id
      in: path
      required: true
      description: Import session ID
      schema:
        type: string
    post:
      summary: Publish import session
      description: Queue ordered chunk processing for a SureImport session. Later
        chunks can reference source IDs mapped by earlier chunks.
      tags:
      - Import Sessions
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '202':
          description: import session publish queued
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ImportSessionResponse"
        '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: max_row_count_exceeded
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '409':
          description: missing expected chunks
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '503':
          description: enqueue failed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: import session 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: []
      - bearerAuth: []
      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
          - ActualImport
          - YnabImport
          - CategoryImport
          - RuleImport
          - MerchantImport
          - PdfImport
          - QifImport
          - SureImport
      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, inline Sure NDJSON content,
        or an uploaded Sure NDJSON file. CSV content is limited to 10MB.
      tags:
      - Imports
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      parameters: []
      responses:
        '201':
          description: import created
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ImportResponse"
        '422':
          description: validation error or publish rejection
          content:
            application/json:
              schema:
                oneOf:
                - "$ref": "#/components/schemas/ErrorResponse"
                - "$ref": "#/components/schemas/ErrorResponseWithImportId"
        '500':
          description: import uploaded but publish enqueue failed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponseWithImportId"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                raw_file_content:
                  type: string
                  description: Raw CSV or Sure NDJSON content as a string. CSV content
                    is limited to 10MB. Required for SureImport unless a multipart
                    file is uploaded.
                type:
                  type: string
                  enum:
                  - TransactionImport
                  - TradeImport
                  - AccountImport
                  - MintImport
                  - ActualImport
                  - YnabImport
                  - CategoryImport
                  - RuleImport
                  - MerchantImport
                  - PdfImport
                  - QifImport
                  - SureImport
                  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: CSV imports only. Header name for the date column
                amount_col_label:
                  type: string
                  description: CSV imports only. Header name for the amount column
                name_col_label:
                  type: string
                  description: CSV imports only. Header name for the transaction name
                    column
                category_col_label:
                  type: string
                  description: CSV imports only. Header name for the category column
                tags_col_label:
                  type: string
                  description: CSV imports only. Header name for the tags column
                notes_col_label:
                  type: string
                  description: CSV imports only. Header name for the notes column
                account_col_label:
                  type: string
                  description: CSV imports only. Header name for the account column
                    when importing rows across multiple accounts
                qty_col_label:
                  type: string
                  description: CSV trade imports only. Header name for the quantity
                    column
                ticker_col_label:
                  type: string
                  description: CSV trade imports only. Header name for the ticker
                    column
                price_col_label:
                  type: string
                  description: CSV trade imports only. Header name for the price column
                entity_type_col_label:
                  type: string
                  description: CSV imports only. Header name for the entity type column
                currency_col_label:
                  type: string
                  description: CSV imports only. Header name for the currency column
                exchange_operating_mic_col_label:
                  type: string
                  description: CSV trade imports only. Header name for the exchange
                    operating MIC column
                date_format:
                  type: string
                  description: CSV imports only. 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: CSV imports only. Number format for parsing amounts
                signage_convention:
                  type: string
                  enum:
                  - inflows_positive
                  - inflows_negative
                  description: CSV imports only. How to interpret positive/negative
                    amounts
                col_sep:
                  type: string
                  enum:
                  - ","
                  - ";"
                  description: CSV imports only. Column separator
                amount_type_strategy:
                  type: string
                  enum:
                  - signed_amount
                  - custom_column
                  description: CSV imports only. Amount parsing strategy
                amount_type_inflow_value:
                  type: string
                  description: CSV imports only. Column value that marks an amount
                    as an inflow when using custom_column strategy
          multipart/form-data:
            schema:
              type: object
              properties:
                raw_file_content:
                  type: string
                  description: Raw CSV or Sure NDJSON content as a string. CSV content
                    is limited to 10MB. Required for SureImport unless a multipart
                    file is uploaded.
                type:
                  type: string
                  enum:
                  - TransactionImport
                  - TradeImport
                  - AccountImport
                  - MintImport
                  - ActualImport
                  - YnabImport
                  - CategoryImport
                  - RuleImport
                  - MerchantImport
                  - PdfImport
                  - QifImport
                  - SureImport
                  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: CSV imports only. Header name for the date column
                amount_col_label:
                  type: string
                  description: CSV imports only. Header name for the amount column
                name_col_label:
                  type: string
                  description: CSV imports only. Header name for the transaction name
                    column
                category_col_label:
                  type: string
                  description: CSV imports only. Header name for the category column
                tags_col_label:
                  type: string
                  description: CSV imports only. Header name for the tags column
                notes_col_label:
                  type: string
                  description: CSV imports only. Header name for the notes column
                account_col_label:
                  type: string
                  description: CSV imports only. Header name for the account column
                    when importing rows across multiple accounts
                qty_col_label:
                  type: string
                  description: CSV trade imports only. Header name for the quantity
                    column
                ticker_col_label:
                  type: string
                  description: CSV trade imports only. Header name for the ticker
                    column
                price_col_label:
                  type: string
                  description: CSV trade imports only. Header name for the price column
                entity_type_col_label:
                  type: string
                  description: CSV imports only. Header name for the entity type column
                currency_col_label:
                  type: string
                  description: CSV imports only. Header name for the currency column
                exchange_operating_mic_col_label:
                  type: string
                  description: CSV trade imports only. Header name for the exchange
                    operating MIC column
                date_format:
                  type: string
                  description: CSV imports only. 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: CSV imports only. Number format for parsing amounts
                signage_convention:
                  type: string
                  enum:
                  - inflows_positive
                  - inflows_negative
                  description: CSV imports only. How to interpret positive/negative
                    amounts
                col_sep:
                  type: string
                  enum:
                  - ","
                  - ";"
                  description: CSV imports only. Column separator
                amount_type_strategy:
                  type: string
                  enum:
                  - signed_amount
                  - custom_column
                  description: CSV imports only. Amount parsing strategy
                amount_type_inflow_value:
                  type: string
                  description: CSV imports only. Column value that marks an amount
                    as an inflow when using custom_column strategy
  "/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, row statistics, and SureImport readback verification when available.
      tags:
      - Imports
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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/imports/{id}/rows":
    parameters:
    - name: id
      in: path
      required: true
      description: Import ID
      schema:
        type: string
    get:
      summary: List import row diagnostics
      description: List sanitized import rows with validation errors and mapping resolution
        state.
      tags:
      - Imports
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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: import rows listed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ImportRowDiagnosticCollection"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: insufficient scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: import not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '500':
          description: internal server error
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/imports/preflight":
    post:
      summary: Validate import content without creating an import
      description: Validate CSV or Sure NDJSON import content and return counts, headers,
        warnings, and validation errors without persisting an import or enqueueing
        jobs. CSV content is limited to 10MB.
      tags:
      - Imports
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      parameters: []
      responses:
        '200':
          description: import content preflighted
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ImportPreflightResponse"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: missing or invalid content
          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:
                raw_file_content:
                  type: string
                  description: Raw CSV or Sure NDJSON content as a string. CSV content
                    is limited to 10MB.
                file:
                  type: string
                  format: binary
                  description: CSV or Sure NDJSON upload when using multipart/form-data.
                    CSV files are limited to 10MB.
                type:
                  type: string
                  enum:
                  - TransactionImport
                  - TradeImport
                  - AccountImport
                  - MintImport
                  - ActualImport
                  - YnabImport
                  - CategoryImport
                  - RuleImport
                  - MerchantImport
                  - PdfImport
                  - QifImport
                  - SureImport
                  description: Import type to validate (defaults to TransactionImport)
                account_id:
                  type: string
                  format: uuid
                  description: Account ID used for account-scoped CSV import validation
                date_col_label:
                  type: string
                  description: CSV imports only. Header name for the date column
                amount_col_label:
                  type: string
                  description: CSV imports only. Header name for the amount column
                name_col_label:
                  type: string
                  description: CSV imports only. Header name for the transaction name
                    column
                category_col_label:
                  type: string
                  description: CSV imports only. Header name for the category column
                tags_col_label:
                  type: string
                  description: CSV imports only. Header name for the tags column
                notes_col_label:
                  type: string
                  description: CSV imports only. Header name for the notes column
                account_col_label:
                  type: string
                  description: CSV imports only. Header name for the account column
                qty_col_label:
                  type: string
                  description: CSV trade imports only. Header name for the quantity
                    column
                ticker_col_label:
                  type: string
                  description: CSV trade imports only. Header name for the ticker
                    column
                price_col_label:
                  type: string
                  description: CSV trade imports only. Header name for the price column
                entity_type_col_label:
                  type: string
                  description: CSV imports only. Header name for the entity type column
                currency_col_label:
                  type: string
                  description: CSV imports only. Header name for the currency column
                exchange_operating_mic_col_label:
                  type: string
                  description: CSV trade imports only. Header name for the exchange
                    operating MIC column
                date_format:
                  type: string
                  description: CSV imports only. Date format pattern
                number_format:
                  type: string
                  enum:
                  - '1,234.56'
                  - 1.234,56
                  - 1 234,56
                  - '1,234'
                  description: CSV imports only. Number format for parsing amounts
                signage_convention:
                  type: string
                  enum:
                  - inflows_positive
                  - inflows_negative
                  description: CSV imports only. How to interpret positive/negative
                    amounts
                col_sep:
                  type: string
                  enum:
                  - ","
                  - ";"
                  description: CSV imports only. Column separator
                rows_to_skip:
                  type: integer
                  minimum: 0
                  description: CSV imports only. Number of leading rows to skip before
                    reading headers
                amount_type_strategy:
                  type: string
                  enum:
                  - signed_amount
                  - custom_column
                  description: CSV imports only. Amount parsing strategy
                amount_type_inflow_value:
                  type: string
                  description: CSV imports only. Column value that marks an amount
                    as an inflow when using custom_column strategy
          multipart/form-data:
            schema:
              type: object
              properties:
                raw_file_content:
                  type: string
                  description: Raw CSV or Sure NDJSON content as a string. CSV content
                    is limited to 10MB.
                file:
                  type: string
                  format: binary
                  description: CSV or Sure NDJSON upload when using multipart/form-data.
                    CSV files are limited to 10MB.
                type:
                  type: string
                  enum:
                  - TransactionImport
                  - TradeImport
                  - AccountImport
                  - MintImport
                  - ActualImport
                  - YnabImport
                  - CategoryImport
                  - RuleImport
                  - MerchantImport
                  - PdfImport
                  - QifImport
                  - SureImport
                  description: Import type to validate (defaults to TransactionImport)
                account_id:
                  type: string
                  format: uuid
                  description: Account ID used for account-scoped CSV import validation
                date_col_label:
                  type: string
                  description: CSV imports only. Header name for the date column
                amount_col_label:
                  type: string
                  description: CSV imports only. Header name for the amount column
                name_col_label:
                  type: string
                  description: CSV imports only. Header name for the transaction name
                    column
                category_col_label:
                  type: string
                  description: CSV imports only. Header name for the category column
                tags_col_label:
                  type: string
                  description: CSV imports only. Header name for the tags column
                notes_col_label:
                  type: string
                  description: CSV imports only. Header name for the notes column
                account_col_label:
                  type: string
                  description: CSV imports only. Header name for the account column
                qty_col_label:
                  type: string
                  description: CSV trade imports only. Header name for the quantity
                    column
                ticker_col_label:
                  type: string
                  description: CSV trade imports only. Header name for the ticker
                    column
                price_col_label:
                  type: string
                  description: CSV trade imports only. Header name for the price column
                entity_type_col_label:
                  type: string
                  description: CSV imports only. Header name for the entity type column
                currency_col_label:
                  type: string
                  description: CSV imports only. Header name for the currency column
                exchange_operating_mic_col_label:
                  type: string
                  description: CSV trade imports only. Header name for the exchange
                    operating MIC column
                date_format:
                  type: string
                  description: CSV imports only. Date format pattern
                number_format:
                  type: string
                  enum:
                  - '1,234.56'
                  - 1.234,56
                  - 1 234,56
                  - '1,234'
                  description: CSV imports only. Number format for parsing amounts
                signage_convention:
                  type: string
                  enum:
                  - inflows_positive
                  - inflows_negative
                  description: CSV imports only. How to interpret positive/negative
                    amounts
                col_sep:
                  type: string
                  enum:
                  - ","
                  - ";"
                  description: CSV imports only. Column separator
                rows_to_skip:
                  type: integer
                  minimum: 0
                  description: CSV imports only. Number of leading rows to skip before
                    reading headers
                amount_type_strategy:
                  type: string
                  enum:
                  - signed_amount
                  - custom_column
                  description: CSV imports only. Amount parsing strategy
                amount_type_inflow_value:
                  type: string
                  description: CSV imports only. Column value that marks an amount
                    as an inflow when using custom_column strategy
  "/api/v1/merchants":
    get:
      summary: List merchants
      tags:
      - Merchants
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: merchants listed
          content:
            application/json:
              schema:
                type: array
                items:
                  "$ref": "#/components/schemas/MerchantDetail"
    post:
      summary: Import merchants from CSV
      tags:
      - Merchants
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      parameters: []
      responses:
        '201':
          description: merchants imported
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/MerchantImportResult"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: missing file or invalid CSV
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        content:
          multipart/form-data:
            schema:
              type: object
              required:
              - file
              properties:
                file:
                  type: string
                  format: binary
        required: true
        description: 'CSV file with columns: name* (required), color, website_url'
  "/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: []
      - bearerAuth: []
      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/provider_connections":
    get:
      summary: Lists provider connection status summaries
      description: List safe provider connection status metadata for the authenticated
        user's family without exposing credentials, raw provider payloads, or raw
        sync errors.
      tags:
      - Provider Connections
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: provider connection status summaries listed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ProviderConnectionCollection"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: insufficient scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/recurring_transactions":
    get:
      summary: List recurring transactions
      tags:
      - Recurring Transactions
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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 recurring status
        schema:
          type: string
          enum:
          - active
          - inactive
      - name: account_id
        in: query
        required: false
        description: Filter by account ID
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: recurring transactions listed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/RecurringTransactionCollection"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: validation error - malformed account filter
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
    post:
      summary: Create recurring transaction
      tags:
      - Recurring Transactions
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      parameters: []
      responses:
        '201':
          description: recurring transaction created
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/RecurringTransaction"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: forbidden - requires read_write scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: account not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: validation error - negative occurrence count
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                recurring_transaction:
                  type: object
                  properties:
                    account_id:
                      type: string
                      format: uuid
                      nullable: true
                    merchant_id:
                      type: string
                      format: uuid
                      nullable: true
                    name:
                      type: string
                      nullable: true
                    amount:
                      type: number
                    currency:
                      type: string
                    expected_day_of_month:
                      type: integer
                      minimum: 1
                      maximum: 31
                    last_occurrence_date:
                      type: string
                      format: date
                    next_expected_date:
                      type: string
                      format: date
                    status:
                      type: string
                      enum:
                      - active
                      - inactive
                    occurrence_count:
                      type: integer
                      minimum: 0
                    manual:
                      type: boolean
                    expected_amount_min:
                      type: number
                      nullable: true
                    expected_amount_max:
                      type: number
                      nullable: true
                    expected_amount_avg:
                      type: number
                      nullable: true
                  required:
                  - amount
                  - currency
                  - expected_day_of_month
                  - last_occurrence_date
                  - next_expected_date
                  anyOf:
                  - required:
                    - name
                  - required:
                    - merchant_id
              required:
              - recurring_transaction
        required: true
  "/api/v1/recurring_transactions/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: Recurring transaction ID
      schema:
        type: string
    get:
      summary: Retrieve recurring transaction
      tags:
      - Recurring Transactions
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: recurring transaction retrieved
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/RecurringTransaction"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: recurring transaction not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
    patch:
      summary: Update recurring transaction
      tags:
      - Recurring Transactions
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      parameters: []
      responses:
        '200':
          description: recurring transaction updated
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/RecurringTransaction"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: forbidden - requires read_write scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: recurring transaction 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:
                recurring_transaction:
                  type: object
                  properties:
                    status:
                      type: string
                      enum:
                      - active
                      - inactive
                    expected_day_of_month:
                      type: integer
                      minimum: 1
                      maximum: 31
                    next_expected_date:
                      type: string
                      format: date
        required: true
    delete:
      summary: Delete recurring transaction
      tags:
      - Recurring Transactions
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: recurring transaction deleted
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/SuccessMessage"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: forbidden - requires read_write scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: recurring transaction not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/rejected_transfers":
    get:
      summary: List rejected transfers
      tags:
      - Rejected Transfers
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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
        schema:
          type: string
          format: uuid
        description: Filter rejected transfers involving this account
      - name: start_date
        in: query
        required: false
        schema:
          type: string
          format: date
        description: Filter rejected transfers from this date
      - name: end_date
        in: query
        required: false
        schema:
          type: string
          format: date
        description: Filter rejected transfers until this date
      responses:
        '200':
          description: rejected transfers listed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/RejectedTransferCollection"
        '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: invalid filter
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/rejected_transfers/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: Rejected transfer ID
      schema:
        type: string
    get:
      summary: Retrieve a rejected transfer
      tags:
      - Rejected Transfers
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: rejected transfer retrieved
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/RejectedTransfer"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: insufficient scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: rejected transfer not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/rule_runs":
    get:
      summary: List rule runs
      description: List rule run history for the authenticated user family.
      tags:
      - Rule Runs
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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: rule_id
        in: query
        required: false
        description: Filter by rule ID
        schema:
          type: string
          format: uuid
      - name: status
        in: query
        required: false
        description: Filter by run status
        schema:
          type: string
          enum:
          - pending
          - success
          - failed
      - name: execution_type
        in: query
        required: false
        description: Filter by execution type
        schema:
          type: string
          enum:
          - manual
          - scheduled
      - name: start_executed_at
        in: query
        required: false
        description: Filter runs executed at or after this timestamp
        schema:
          type: string
          format: date-time
      - name: end_executed_at
        in: query
        required: false
        description: Filter runs executed at or before this timestamp
        schema:
          type: string
          format: date-time
      responses:
        '200':
          description: rule runs listed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/RuleRunCollection"
        '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: invalid filter
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/rule_runs/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: Rule run ID
      schema:
        type: string
        format: uuid
    get:
      summary: Retrieve a rule run
      description: Retrieve one rule run from the authenticated user family.
      tags:
      - Rule Runs
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: rule run retrieved
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/RuleRunResponse"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: insufficient scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: rule run not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/rules":
    get:
      summary: List rules
      tags:
      - Rules
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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: resource_type
        in: query
        required: false
        description: Filter by rule resource type
        schema:
          type: string
          enum:
          - transaction
      - name: active
        in: query
        required: false
        description: Filter by active status
        schema:
          type: boolean
      responses:
        '200':
          description: rules listed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/RuleCollection"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: forbidden - requires read scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: unsupported resource type
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/rules/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: Rule ID
      schema:
        type: string
    get:
      summary: Retrieve a rule
      tags:
      - Rules
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: rule retrieved
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/RuleResponse"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: forbidden - requires read scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: rule not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/securities":
    get:
      summary: List securities referenced by family investment data
      tags:
      - Securities
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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: ticker
        in: query
        required: false
        description: Filter by ticker symbol
        schema:
          type: string
      - name: exchange_operating_mic
        in: query
        required: false
        description: Filter by exchange operating MIC
        schema:
          type: string
      - name: kind
        in: query
        required: false
        description: Filter by security kind
        schema:
          type: string
          enum:
          - standard
          - cash
      - name: offline
        in: query
        required: false
        description: Filter by offline status. When supplied, must be true or false.
        schema:
          type: boolean
      responses:
        '200':
          description: securities listed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/SecurityCollection"
        '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: invalid filter
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/securities/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: Security ID
      schema:
        type: string
        format: uuid
    get:
      summary: Retrieve a security referenced by family investment data
      tags:
      - Securities
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: security retrieved
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Security"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: insufficient scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: security not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/security_prices":
    get:
      summary: List security price history referenced by family investment data
      tags:
      - Security Prices
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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: security_id
        in: query
        required: false
        description: Filter by security ID
        schema:
          type: string
          format: uuid
      - name: currency
        in: query
        required: false
        description: Filter by currency code
        schema:
          type: string
      - name: start_date
        in: query
        required: false
        description: Filter prices from this date
        schema:
          type: string
          format: date
      - name: end_date
        in: query
        required: false
        description: Filter prices until this date
        schema:
          type: string
          format: date
      - name: provisional
        in: query
        required: false
        description: Filter by provisional price status. When supplied, must be true
          or false.
        schema:
          type: boolean
      responses:
        '200':
          description: security prices listed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/SecurityPriceCollection"
        '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: invalid filter
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/security_prices/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: Security price ID
      schema:
        type: string
        format: uuid
    get:
      summary: Retrieve a security price referenced by family investment data
      tags:
      - Security Prices
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: security price retrieved
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/SecurityPrice"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: insufficient scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: security price not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/sync":
    post:
      summary: Trigger a full family sync
      description: >
        Enqueues a background job that applies all active rules, syncs all connected
        accounts, and auto-matches transfers for the authenticated user's family.
        Returns HTTP 202 Accepted with the queued sync object. Requires write scope.
      tags:
      - Sync
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '202':
          description: sync queued
          content:
            application/json:
              schema:
                type: object
                required:
                - id
                - status
                - message
                properties:
                  id:
                    type: string
                    format: uuid
                  status:
                    type: string
                    example: pending
                  syncable_type:
                    type: string
                    nullable: true
                  syncable_id:
                    type: string
                    format: uuid
                    nullable: true
                  syncing_at:
                    type: string
                    format: date-time
                    nullable: true
                  completed_at:
                    type: string
                    format: date-time
                    nullable: true
                  window_start_date:
                    type: string
                    format: date
                    nullable: true
                  window_end_date:
                    type: string
                    format: date
                    nullable: true
                  message:
                    type: string
                    example: Sync has been queued and will apply all active rules
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: insufficient scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/usage":
    get:
      summary: Retrieve API usage and rate limit info
      description: >
        Returns rate limit status and usage information for the current API key.
        For OAuth authentication, returns a brief message indicating that detailed
        usage tracking is only available for API key authentication.
        Requires read scope.
      tags:
      - Usage
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: usage information returned
          content:
            application/json:
              schema:
                oneOf:
                - type: object
                  description: Response when authenticated with an API key
                  required:
                  - api_key
                  - rate_limit
                  properties:
                    api_key:
                      type: object
                      required:
                      - name
                      - scopes
                      - created_at
                      properties:
                        name:
                          type: string
                        scopes:
                          type: array
                          items:
                            type: string
                        last_used_at:
                          type: string
                          format: date-time
                          nullable: true
                        created_at:
                          type: string
                          format: date-time
                    rate_limit:
                      type: object
                      required:
                      - tier
                      - limit
                      - current_count
                      - remaining
                      - reset_in_seconds
                      - reset_at
                      properties:
                        tier:
                          type: string
                          enum:
                          - standard
                          - premium
                          - enterprise
                        limit:
                          type: integer
                          description: Maximum requests allowed per hour
                        current_count:
                          type: integer
                          description: Requests made in the current hour window
                        remaining:
                          type: integer
                          description: Requests remaining in the current window
                        reset_in_seconds:
                          type: integer
                          description: Seconds until the rate limit window resets
                        reset_at:
                          type: string
                          format: date-time
                          description: Timestamp when the rate limit window resets
                - type: object
                  description: Response when authenticated with OAuth
                  required:
                  - authentication_method
                  - message
                  properties:
                    authentication_method:
                      type: string
                      example: oauth
                    message:
                      type: string
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: insufficient scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/syncs":
    get:
      summary: Lists sync history
      description: List sanitized sync status history for the authenticated user's
        family, accounts, and provider connections.
      tags:
      - Syncs
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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: syncs listed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/SyncCollection"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: forbidden
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/syncs/latest":
    get:
      summary: Shows the latest sync
      description: 'Return the most recently created sanitized sync status for the
        authenticated user''s family, or data: null when no sync exists.'
      tags:
      - Syncs
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: latest sync shown
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/SyncResponse"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: forbidden
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/syncs/{id}":
    parameters:
    - name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    get:
      summary: Shows a sync
      description: Return sanitized status metadata for a single family-scoped sync.
      tags:
      - Syncs
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: sync shown
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/SyncResponse"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: forbidden
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/tags":
    get:
      summary: List tags
      tags:
      - Tags
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: tags listed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/TagCollection"
    post:
      summary: Create tag
      tags:
      - Tags
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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: []
      - bearerAuth: []
      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: []
      - bearerAuth: []
      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: []
      - bearerAuth: []
      responses:
        '204':
          description: tag deleted
        '404':
          description: tag not found
  "/api/v1/trades":
    get:
      summary: List trades
      tags:
      - Trades
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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: []
      - bearerAuth: []
      parameters: []
      responses:
        '201':
          description: interest created
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/TransactionResponse"
        '403':
          description: forbidden - api key missing read_write scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '401':
          description: unauthorized - missing api key
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: deposit without amount returns error
          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 for buy/sell)
                    price:
                      type: number
                      description: Price (required for buy/sell)
                    amount:
                      type: number
                      description: Amount (required for dividend, deposit, withdrawal,
                        interest)
                    type:
                      type: string
                      enum:
                      - buy
                      - sell
                      - dividend
                      - deposit
                      - withdrawal
                      - interest
                      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
                    transfer_account_id:
                      type: string
                      format: uuid
                      description: Destination/source account ID for linked transfers
                  required:
                  - account_id
                  - date
                  - 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: []
      - bearerAuth: []
      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: []
      - bearerAuth: []
      parameters: []
      responses:
        '200':
          description: trade updated
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Trade"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: forbidden - api key missing read_write scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '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
                      - dividend
                      - deposit
                      - withdrawal
                      - interest
                    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: []
      - bearerAuth: []
      responses:
        '200':
          description: trade deleted
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/DeleteResponse"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: forbidden - api key missing read_write scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: trade not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/transactions":
    get:
      summary: List transactions
      tags:
      - Transactions
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      description: Returns global ledger history for accessible accounts, including
        disabled accounts but excluding accounts pending deletion.
      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: []
      - bearerAuth: []
      parameters: []
      responses:
        '201':
          description: transaction created
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Transaction"
        '200':
          description: transaction already exists for external idempotency key
          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)
                    external_id:
                      type: string
                      description: Optional external idempotency key scoped to account
                        and source
                    source:
                      type: string
                      description: Optional source namespace for external_id. Requires
                        external_id and defaults to api when external_id is provided
                    tag_ids:
                      type: array
                      items:
                        type: string
                        format: uuid
                      description: Array of tag IDs
                    user_modified:
                      type: boolean
                      description: >
                        Set to true to mark this transaction as user-modified. When set,
                        the next bank sync will not overwrite the fields this API client
                        has set (name, category, etc.), giving the same sync-protection
                        as a manual edit. Useful when writing into an account that is
                        also linked to a bank-sync provider.
                  required:
                  - account_id
                  - date
                  - amount
                  - name
              required:
              - transaction
        required: true
  "/api/v1/transactions/{id}":
    parameters:
    - name: id
      in: path
      schema:
        type: string
        format: uuid
      required: true
      description: Transaction ID
    get:
      summary: Retrieve a transaction
      tags:
      - Transactions
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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: []
      - bearerAuth: []
      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: []
      - bearerAuth: []
      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/transfers":
    get:
      summary: List transfers
      tags:
      - Transfers
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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
        schema:
          type: string
          enum:
          - pending
          - confirmed
        description: Filter by transfer status
      - name: account_id
        in: query
        required: false
        schema:
          type: string
          format: uuid
        description: Filter transfers involving this account
      - name: start_date
        in: query
        required: false
        schema:
          type: string
          format: date
        description: Filter transfers from this date
      - name: end_date
        in: query
        required: false
        schema:
          type: string
          format: date
        description: Filter transfers until this date
      responses:
        '200':
          description: transfers listed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/TransferDecisionCollection"
        '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: invalid filter
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/transfers/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: Transfer ID
      schema:
        type: string
    get:
      summary: Retrieve a transfer
      tags:
      - Transfers
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: transfer retrieved
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/TransferDecision"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: insufficient scope
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '404':
          description: transfer not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/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. The returned job_id is informational
        only; reset status is family-scoped, not job-scoped. Requires admin role.
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: account reset initiated
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ResetInitiatedResponse"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: forbidden - requires read_write scope and admin role
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '500':
          description: reset enqueue failed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/users/reset/status":
    get:
      summary: Retrieve reset status
      tags:
      - Users
      description: Returns counts of family-owned data targeted by account reset.
        Use this after DELETE /api/v1/users/reset to decide whether reset materialization
        has completed. Completion is a counts-based family snapshot and may change
        if new data is created after reset.
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: reset status returned
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ResetStatusResponse"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '403':
          description: forbidden - requires 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: []
      - bearerAuth: []
      responses:
        '200':
          description: account deleted
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/SuccessMessage"
        '401':
          description: unauthorized
        '403':
          description: insufficient scope
        '422':
          description: deactivation failed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/valuations":
    get:
      summary: List valuations
      tags:
      - Valuations
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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
          format: uuid
      - name: start_date
        in: query
        required: false
        description: Filter valuations from this date
        schema:
          type: string
          format: date
      - name: end_date
        in: query
        required: false
        description: Filter valuations until this date
        schema:
          type: string
          format: date
      responses:
        '200':
          description: valuations listed
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ValuationCollection"
        '401':
          description: unauthorized
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
        '422':
          description: invalid account filter
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
    post:
      summary: Create valuation
      tags:
      - Valuations
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      parameters: []
      responses:
        '201':
          description: valuation created
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Valuation"
        '200':
          description: existing valuation upserted
          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
                    upsert:
                      type: boolean
                      description: Nested alternative to the top-level response-status
                        flag. Top-level upsert takes precedence when both are provided.
                  required:
                  - account_id
                  - amount
                  - date
                upsert:
                  type: boolean
                  description: Response-status signal only. When true and a same-account
                    same-date valuation exists before the request, the endpoint returns
                    200 OK instead of 201 Created. The underlying reconciliation write
                    path is unchanged; this flag does not add duplicate-prevention
                    or safe-retry guarantees beyond existing same-date reconciliation
                    behavior.
              required:
              - valuation
        required: true
  "/api/v1/valuations/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: Valuation ID (entry ID)
      schema:
        type: string
    get:
      summary: Retrieve a valuation
      tags:
      - Valuations
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      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: []
      - bearerAuth: []
      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/insights":
    get:
      summary: List insights
      description: >
        Returns all visible (active and read) insights for the authenticated user's family,
        ordered by priority then recency. Requires preview features to be enabled for the user.
      tags:
      - Insights
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '200':
          description: insights retrieved
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/InsightCollection"
        '403':
          description: preview features not enabled
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/push_subscriptions":
    post:
      summary: Register a push subscription
      description: >
        Registers an APNs device token for push notifications. If the token already
        exists for the user it is updated in place. Requires write scope.
      tags:
      - Push Subscriptions
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - token
              - environment
              - platform
              properties:
                token:
                  type: string
                  description: >
                    APNs device token (hex string, 64–200 lowercase hex characters).
                environment:
                  type: string
                  enum:
                  - sandbox
                  - production
                  description: APNs environment the token belongs to.
                platform:
                  type: string
                  enum:
                  - ios
                  description: The platform of the device. Currently only `ios` is supported.
      responses:
        '201':
          description: push subscription registered or updated
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/PushSubscription"
        '422':
          description: validation error
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
  "/api/v1/push_subscriptions/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: Push subscription ID
      schema:
        type: string
    delete:
      summary: Remove a push subscription
      description: >
        Unregisters a device token. Call this when the user signs out or revokes
        notification permission so the device stops receiving push notifications.
        Requires write scope.
      tags:
      - Push Subscriptions
      security:
      - apiKeyAuth: []
      - bearerAuth: []
      responses:
        '204':
          description: push subscription removed
        '404':
          description: push subscription not found
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
