openapi: 3.1.0
info:
  title: Delauretis Personal Finance API
  version: 1.0.0
  description: |
    Production-grade Personal Finance API and high-integrity analytical replica for Delauretis.
    Enforces atomic batch ingestion, idempotency, CAS revision control, and strict tenant isolation.
    Human access is restricted exclusively to verified Google identity: nicolasdelauretis122@gmail.com.
    Machine ingestion is authenticated via scoped API Keys for GrokBot and Mango.
servers:
  - url: https://api.delauretis.com.ar
    description: Production Edge Worker
  - url: https://delauretis.com.ar
    description: Primary Domain Proxy

paths:
  /v1/finances/changes:
    post:
      summary: Machine Batch Ingestion
      description: |
        Ingests a batch of mutations (upsert/delete) across categories, accounts, movements, refund links, installments, and budgets.
        Operates atomically with durable idempotency replay and CAS revision verification.
      security:
        - ApiKeyAuth: []
        - BearerAuth: []
      parameters:
        - in: header
          name: Idempotency-Key
          schema:
            type: string
          required: false
          description: Client-provided UUID matching payload request_id.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/IngestionBatchRequest'
      responses:
        '200':
          description: Batch applied or idempotent replay of existing receipt.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IngestionBatchResult'
        '400':
          $ref: '#/components/responses/400BadRequest'
        '401':
          $ref: '#/components/responses/401Unauthorized'
        '409':
          $ref: '#/components/responses/409Conflict'
        '413':
          $ref: '#/components/responses/413PayloadTooLarge'
        '422':
          $ref: '#/components/responses/422Unprocessable'

  /v1/finances/api-keys:
    post:
      summary: Register Machine API Key
      description: |
        Allows Nicolás De Lauretis to register a new custom API Key for GrokBot or Mango.
        Stored hashed (SHA-256) in the database with a safe prefix.
      security:
        - GoogleOAuthAccess: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
                - name
                - rawKey
              properties:
                name:
                  type: string
                  example: GrokBot Notion Ingestion
                rawKey:
                  type: string
                  example: gb_secret_prod_xyz123
                scope:
                  type: string
                  default: finance:ingest
      responses:
        '200':
          description: API Key registered successfully.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  message:
                    type: string
                  key_id:
                    type: string
                  key_prefix:
                    type: string
        '401':
          $ref: '#/components/responses/401Unauthorized'
        '403':
          $ref: '#/components/responses/403Forbidden'

  /v1/finances/movements:
    get:
      summary: List Movements
      description: |
        Retrieves a paginated list of financial movements. Excludes soft-deleted records and duplicates/aliases.
      security:
        - GoogleOAuthAccess: []
      parameters:
        - in: query
          name: from
          schema:
            type: string
            format: date
          example: '2026-11-01'
        - in: query
          name: to
          schema:
            type: string
            format: date
          example: '2026-11-30'
        - in: query
          name: status
          schema:
            type: string
            enum: [pending, estimated, confirmed]
        - in: query
          name: kind
          schema:
            type: string
            enum: [income, expense, transfer_own, card_payment, refund]
        - in: query
          name: category_id
          schema:
            type: string
        - in: query
          name: account_id
          schema:
            type: string
        - in: query
          name: currency
          schema:
            type: string
            example: ARS
        - in: query
          name: cursor
          schema:
            type: string
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
            maximum: 100
      responses:
        '200':
          description: Paginated movements data.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Movement'
                  pagination:
                    $ref: '#/components/schemas/Pagination'
                  meta:
                    type: object

  /v1/finances/movements/{id}:
    get:
      summary: Get Movement Detail
      description: Retrieves single movement with linked refund allocations, installment schedules, and external source references.
      security:
        - GoogleOAuthAccess: []
      parameters:
        - in: path
          name: id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Detailed movement object.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/MovementDetail'
        '404':
          $ref: '#/components/responses/404NotFound'

  /v1/finances/categories:
    get:
      summary: List Categories
      description: Retrieves category tree hierarchy. Supports parent groups like Auto rolling up child categories.
      security:
        - GoogleOAuthAccess: []
      parameters:
        - in: query
          name: include_inactive
          schema:
            type: boolean
            default: false
      responses:
        '200':
          description: Categories list.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Category'

  /v1/finances/accounts:
    get:
      summary: List Financial Accounts
      description: Retrieves user accounts (bank, cash, wallet, credit card, investment).
      security:
        - GoogleOAuthAccess: []
      parameters:
        - in: query
          name: include_inactive
          schema:
            type: boolean
            default: false
      responses:
        '200':
          description: Accounts list.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Account'

  /v1/finances/budgets:
    get:
      summary: Monthly Budget Progress
      description: Calculates budget spending, remaining limits, and progress percentages by category.
      security:
        - GoogleOAuthAccess: []
      parameters:
        - in: query
          name: month
          schema:
            type: string
            example: '2026-11'
        - in: query
          name: currency
          schema:
            type: string
            default: ARS
      responses:
        '200':
          description: Budget items with spent minor arithmetic.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/BudgetItem'

  /v1/finances/installments:
    get:
      summary: Projected Installments Schedule
      description: Future credit card and payment installments grouped by due month. Future projections never duplicate historical expenses.
      security:
        - GoogleOAuthAccess: []
      parameters:
        - in: query
          name: currency
          schema:
            type: string
            default: ARS
      responses:
        '200':
          description: Installment schedule by month.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/InstallmentMonth'

  /v1/finances/reports/evolution:
    get:
      summary: Financial Evolution Time-Series
      description: Daily or monthly breakdown showing gross incurred expenses, adjusted original expenses (deducting confirmed refunds), incomes, and net cashflow.
      security:
        - GoogleOAuthAccess: []
      parameters:
        - in: query
          name: bucket
          schema:
            type: string
            enum: [day, month]
            default: month
        - in: query
          name: currency
          schema:
            type: string
            default: ARS
        - in: query
          name: from
          schema:
            type: string
            format: date
        - in: query
          name: to
          schema:
            type: string
            format: date
      responses:
        '200':
          description: Time-series evolution data.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/EvolutionItem'

  /v1/finances/reports/categories:
    get:
      summary: Category Spending Breakdown & Rollup
      description: Hierarchical category spending report. Auto rolls up Combustible, Peajes, and Seguro without duplicate counting.
      security:
        - GoogleOAuthAccess: []
      parameters:
        - in: query
          name: month
          schema:
            type: string
            example: '2026-11'
        - in: query
          name: currency
          schema:
            type: string
            default: ARS
        - in: query
          name: basis
          schema:
            type: string
            enum: [adjusted_original, gross_incurred]
            default: adjusted_original
      responses:
        '200':
          description: Category spending breakdown.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/CategorySpendingItem'

  /v1/finances/reports/summary:
    get:
      summary: Macro Financial Summary
      description: Top-level monthly totals for the dashboard (incomes, gross expenses, refunds collected, adjusted expenses, and net cashflow).
      security:
        - GoogleOAuthAccess: []
      parameters:
        - in: query
          name: month
          schema:
            type: string
            example: '2026-11'
        - in: query
          name: currency
          schema:
            type: string
            default: ARS
      responses:
        '200':
          description: Macro summary.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    $ref: '#/components/schemas/MacroSummary'

  /v1/finances/reports/cashflow:
    get:
      summary: Account Cashflow
      description: Real inflows, outflows, and net balances per account using settled settlement dates.
      security:
        - GoogleOAuthAccess: []
      parameters:
        - in: query
          name: from
          schema:
            type: string
        - in: query
          name: to
          schema:
            type: string
      responses:
        '200':
          description: Cashflow by account.
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/AccountCashflow'

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: Scoped API Key for GrokBot and Mango ingestion.
    BearerAuth:
      type: http
      scheme: bearer
      description: Bearer token format for machine ingestion.
    GoogleOAuthAccess:
      type: openIdConnect
      openIdConnectUrl: https://accounts.google.com/.well-known/openid-configuration
      description: Verified Google identity strictly restricted to nicolasdelauretis122@gmail.com.

  schemas:
    IngestionBatchRequest:
      type: object
      required:
        - schema_version
        - request_id
        - items
      properties:
        schema_version:
          type: integer
          enum: [1]
        request_id:
          type: string
          example: req-2026-11-10-001
        atomic:
          type: boolean
          default: true
        items:
          type: array
          maxItems: 50
          items:
            $ref: '#/components/schemas/IngestionBatchItem'

    IngestionBatchItem:
      type: object
      required:
        - event_id
        - entity_type
        - entity_id
        - revision
        - operation
        - payload
      properties:
        event_id:
          type: string
          example: evt-101
        entity_type:
          type: string
          enum: [category, account, movement, refund_link, installment, budget]
        entity_id:
          type: string
          example: mov-coto-101
        revision:
          type: integer
          minimum: 1
        operation:
          type: string
          enum: [upsert, delete]
        payload:
          type: object

    IngestionBatchResult:
      type: object
      properties:
        request_id:
          type: string
        committed:
          type: boolean
        processed_count:
          type: integer
        results:
          type: array
          items:
            type: object
            properties:
              event_id:
                type: string
              entity_type:
                type: string
              entity_id:
                type: string
              status:
                type: string
                enum: [applied, duplicate, stale_rejected]
              current_revision:
                type: integer

    Movement:
      type: object
      properties:
        id:
          type: string
        kind:
          type: string
          enum: [income, expense, transfer_own, card_payment, refund]
        status:
          type: string
          enum: [pending, estimated, confirmed]
        description:
          type: string
        merchant:
          type: string
          nullable: true
        occurred_on:
          type: string
          format: date
        amount_minor:
          type: integer
          description: Positive integer in minor units (e.g. 10000 = $100.00)
        currency:
          type: string
        category_id:
          type: string
          nullable: true
        from_account_id:
          type: string
          nullable: true
        to_account_id:
          type: string
          nullable: true
        settled_on:
          type: string
          nullable: true
        revision:
          type: integer

    MovementDetail:
      allOf:
        - $ref: '#/components/schemas/Movement'
        - type: object
          properties:
            refund_links:
              type: array
              items:
                type: object
            installments:
              type: array
              items:
                type: object
            sources:
              type: array
              items:
                type: object

    Category:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        parent_id:
          type: string
          nullable: true
        active:
          type: integer
        revision:
          type: integer

    Account:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        type:
          type: string
          enum: [cash, bank, wallet_balance, credit_card, investment]
        currency:
          type: string
          nullable: true
        active:
          type: integer

    BudgetItem:
      type: object
      properties:
        id:
          type: string
        category_id:
          type: string
        category_name:
          type: string
        month:
          type: string
        limit_minor:
          type: integer
        spent_minor:
          type: integer
        remaining_minor:
          type: integer
        percentage:
          type: integer

    InstallmentMonth:
      type: object
      properties:
        due_month:
          type: string
        count:
          type: integer
        total_amount_minor:
          type: integer
        currency:
          type: string
        items:
          type: array
          items:
            type: object

    EvolutionItem:
      type: object
      properties:
        period:
          type: string
        currency:
          type: string
        gross_incurred_minor:
          type: integer
        confirmed_expenses_minor:
          type: integer
        estimated_expenses_minor:
          type: integer
        incomes_minor:
          type: integer
        refunds_collected_minor:
          type: integer
        adjusted_original_minor:
          type: integer
        net_cashflow_minor:
          type: integer
        missing_fx_count:
          type: integer

    CategorySpendingItem:
      type: object
      properties:
        category_id:
          type: string
        name:
          type: string
        parent_id:
          type: string
          nullable: true
        direct_spent_minor:
          type: integer
        total_spent_minor:
          type: integer
        currency:
          type: string

    MacroSummary:
      type: object
      properties:
        month:
          type: string
        currency:
          type: string
        incomes_minor:
          type: integer
        gross_expenses_minor:
          type: integer
        adjusted_expenses_minor:
          type: integer
        refunds_collected_minor:
          type: integer
        net_cashflow_minor:
          type: integer
        missing_fx_count:
          type: integer

    AccountCashflow:
      type: object
      properties:
        account_id:
          type: string
        account_name:
          type: string
        account_type:
          type: string
        currency:
          type: string
          nullable: true
        inflows_minor:
          type: integer
        outflows_minor:
          type: integer
        net_change_minor:
          type: integer

    Pagination:
      type: object
      properties:
        cursor:
          type: string
          nullable: true
        has_more:
          type: boolean
        total:
          type: integer
        limit:
          type: integer

  responses:
    400BadRequest:
      description: Bad Request - Missing required parameters or malformed payload.
    401Unauthorized:
      description: Unauthorized - Valid credentials required.
    403Forbidden:
      description: Forbidden - Identity not authorized (must be nicolasdelauretis122@gmail.com).
    404NotFound:
      description: Not Found - Requested resource does not exist.
    409Conflict:
      description: Conflict - CAS revision conflict or idempotency key collision.
    413PayloadTooLarge:
      description: Payload Too Large - Exceeds batch maximum of 50 items.
    422Unprocessable:
      description: Unprocessable Entity - Validation invariant failure.
