---
openapi: 3.0.3
info:
  title: Sure API
  version: v1
  description: OpenAPI documentation generated from executable request specs.
servers:
- url: https://api.sure.app
  description: Production
- url: http://localhost:3000
  description: Local development
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
  schemas:
    Pagination:
      type: object
      required:
      - page
      - per_page
      - total_count
      - total_pages
      properties:
        page:
          type: integer
          minimum: 1
        per_page:
          type: integer
          minimum: 1
        total_count:
          type: integer
          minimum: 0
        total_pages:
          type: integer
          minimum: 0
    ErrorResponse:
      type: object
      required:
      - error
      properties:
        error:
          type: string
        message:
          type: string
          nullable: true
        details:
          oneOf:
          - type: array
            items:
              type: string
          - type: object
          nullable: true
    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
    Category:
      type: object
      required:
      - id
      - name
      - classification
      - color
      - icon
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        classification:
          type: string
        color:
          type: string
        icon:
          type: string
    CategoryParent:
      type: object
      required:
      - id
      - name
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
    CategoryDetail:
      type: object
      required:
      - id
      - name
      - classification
      - color
      - icon
      - subcategories_count
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        classification:
          type: string
          enum:
          - income
          - expense
        color:
          type: string
        icon:
          type: string
        parent:
          "$ref": "#/components/schemas/CategoryParent"
          nullable: true
        subcategories_count:
          type: integer
          minimum: 0
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    CategoryCollection:
      type: object
      required:
      - categories
      - pagination
      properties:
        categories:
          type: array
          items:
            "$ref": "#/components/schemas/CategoryDetail"
        pagination:
          "$ref": "#/components/schemas/Pagination"
    Merchant:
      type: object
      required:
      - id
      - name
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
    Tag:
      type: object
      required:
      - id
      - name
      - color
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        color:
          type: string
    Transfer:
      type: object
      required:
      - id
      - amount
      - currency
      properties:
        id:
          type: string
          format: uuid
        amount:
          type: string
        currency:
          type: string
        other_account:
          "$ref": "#/components/schemas/Account"
          nullable: true
    Transaction:
      type: object
      required:
      - id
      - date
      - amount
      - currency
      - name
      - classification
      - account
      - tags
      - created_at
      - updated_at
      properties:
        id:
          type: string
          format: uuid
        date:
          type: string
          format: date
        amount:
          type: string
        currency:
          type: string
        name:
          type: string
        notes:
          type: string
          nullable: true
        classification:
          type: string
        account:
          "$ref": "#/components/schemas/Account"
        category:
          "$ref": "#/components/schemas/Category"
          nullable: true
        merchant:
          "$ref": "#/components/schemas/Merchant"
          nullable: true
        tags:
          type: array
          items:
            "$ref": "#/components/schemas/Tag"
        transfer:
          "$ref": "#/components/schemas/Transfer"
          nullable: true
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    TransactionCollection:
      type: object
      required:
      - transactions
      - pagination
      properties:
        transactions:
          type: array
          items:
            "$ref": "#/components/schemas/Transaction"
        pagination:
          "$ref": "#/components/schemas/Pagination"
    DeleteResponse:
      type: object
      required:
      - message
      properties:
        message:
          type: string
paths:
  "/api/v1/categories":
    get:
      summary: List categories
      tags:
      - Categories
      security:
      - bearerAuth: []
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        description: Bearer token with read scope
      - name: page
        in: query
        required: false
        description: 'Page number (default: 1)'
        schema:
          type: integer
      - name: per_page
        in: query
        required: false
        description: 'Items per page (default: 25, max: 100)'
        schema:
          type: integer
      - name: classification
        in: query
        required: false
        description: Filter by classification (income or expense)
        schema:
          type: string
          enum:
          - income
          - expense
      - name: roots_only
        in: query
        required: false
        description: Return only root categories (no parent)
        schema:
          type: boolean
      - name: parent_id
        in: query
        required: false
        description: Filter by parent category ID
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: categories filtered by parent
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/CategoryCollection"
  "/api/v1/categories/{id}":
    parameters:
    - name: Authorization
      in: header
      required: true
      schema:
        type: string
      description: Bearer token with read scope
    - name: id
      in: path
      required: true
      description: Category ID
      schema:
        type: string
    get:
      summary: Retrieve a category
      tags:
      - Categories
      security:
      - 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:
      - bearerAuth: []
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        description: Bearer token with read scope
      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:
      - bearerAuth: []
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        description: Bearer token with write scope
      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: Authorization
      in: header
      required: true
      schema:
        type: string
      description: Bearer token with read scope
    - name: id
      in: path
      required: true
      description: Chat ID
      schema:
        type: string
    get:
      summary: Retrieve a chat
      tags:
      - Chats
      security:
      - 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:
      - 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:
      - bearerAuth: []
      responses:
        '204':
          description: chat deleted
        '404':
          description: chat not found
  "/api/v1/chats/{chat_id}/messages":
    parameters:
    - name: Authorization
      in: header
      required: true
      schema:
        type: string
      description: Bearer token with write scope
    - name: chat_id
      in: path
      required: true
      description: Chat ID
      schema:
        type: string
    post:
      summary: Create a message
      tags:
      - Chat Messages
      security:
      - 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: Authorization
      in: header
      required: true
      schema:
        type: string
      description: Bearer token with write scope
    - name: chat_id
      in: path
      required: true
      description: Chat ID
      schema:
        type: string
    post:
      summary: Retry the last assistant response
      tags:
      - Chat Messages
      security:
      - 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/transactions":
    get:
      summary: List transactions
      tags:
      - Transactions
      security:
      - bearerAuth: []
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        description: Bearer token with read scope
      - 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:
      - bearerAuth: []
      parameters:
      - name: Authorization
        in: header
        required: true
        schema:
          type: string
        description: Bearer token with write scope
      responses:
        '201':
          description: transaction created
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/Transaction"
        '422':
          description: validation error - missing required fields
          content:
            application/json:
              schema:
                "$ref": "#/components/schemas/ErrorResponse"
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                transaction:
                  type: object
                  properties:
                    account_id:
                      type: string
                      format: uuid
                      description: Account ID (required)
                    date:
                      type: string
                      format: date
                      description: Transaction date
                    amount:
                      type: number
                      description: Transaction amount
                    name:
                      type: string
                      description: Transaction name/description
                    description:
                      type: string
                      description: Alternative to name field
                    notes:
                      type: string
                      description: Additional notes
                    currency:
                      type: string
                      description: Currency code (defaults to family currency)
                    category_id:
                      type: string
                      format: uuid
                      description: Category ID
                    merchant_id:
                      type: string
                      format: uuid
                      description: Merchant ID
                    nature:
                      type: string
                      enum:
                      - income
                      - expense
                      - inflow
                      - outflow
                      description: Transaction nature (determines sign)
                    tag_ids:
                      type: array
                      items:
                        type: string
                        format: uuid
                      description: Array of tag IDs
                  required:
                  - account_id
                  - date
                  - amount
                  - name
              required:
              - transaction
        required: true
  "/api/v1/transactions/{id}":
    parameters:
    - name: Authorization
      in: header
      required: true
      schema:
        type: string
      description: Bearer token
    - name: id
      in: path
      required: true
      description: Transaction ID
      schema:
        type: string
    get:
      summary: Retrieve a transaction
      tags:
      - Transactions
      security:
      - 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:
      - 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
        required: true
    delete:
      summary: Delete a transaction
      tags:
      - Transactions
      security:
      - 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"
