openapi: 3.1.0
info:
  title: Talea Vivendi Integration API
  version: 1.0.0
  description: >
    JSON/HTTP API for n8n, MCP servers, Claude, and Codex integrations.
    Use this API as the primary machine interface. The gRPC endpoint remains available for legacy clients.
servers:
  - url: https://vivendi.example.com
    description: Production
  - url: http://localhost:5000
    description: Local development
tags:
  - name: Auth
  - name: Invoices
  - name: Duties
security:
  - bearerAuth: []
paths:
  /api/v1/auth/token:
    post:
      tags: [Auth]
      security: []
      summary: Exchange Vivendi user credentials for an API access token
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAccessTokenRequest'
      responses:
        '200':
          description: Access token created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AccessTokenResponse'
        '401':
          description: Invalid credentials
  /api/v1/invoices:
    get:
      tags: [Invoices]
      summary: Load invoices by invoice number or list open/all invoices
      parameters:
        - name: invoiceNumber
          in: query
          required: false
          description: Single invoice number or comma-separated list of invoice numbers
          schema:
            type: string
        - name: openOnly
          in: query
          required: false
          schema:
            type: boolean
            default: false
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 1000
            default: 250
      responses:
        '200':
          description: Invoice list
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/InvoiceResponse'
  /api/v1/duties:
    get:
      tags: [Duties]
      summary: Load duties using all DutyQuery filter fields
      parameters:
        - name: dutyFrom
          in: query
          required: false
          schema:
            type: string
            format: date-time
        - name: dutyTo
          in: query
          required: false
          schema:
            type: string
            format: date-time
        - name: employeeNumber
          in: query
          required: false
          schema:
            type: string
        - name: dutyCodes
          in: query
          required: false
          description: Single duty code or comma-separated list of duty codes
          schema:
            type: string
        - name: changedFrom
          in: query
          required: false
          schema:
            type: string
            format: date-time
        - name: changedTo
          in: query
          required: false
          schema:
            type: string
            format: date-time
      responses:
        '200':
          description: Duty list
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/DutyResponse'
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
  schemas:
    CreateAccessTokenRequest:
      type: object
      required: [username, password]
      properties:
        username:
          type: string
        password:
          type: string
          format: password
    AccessTokenResponse:
      type: object
      required: [accessToken, tokenType, expiresIn, username, scopes]
      properties:
        accessToken:
          type: string
        tokenType:
          type: string
          example: Bearer
        expiresIn:
          type: integer
          description: Lifetime in seconds
        username:
          type: string
        scopes:
          type: array
          items:
            type: string
    InvoiceResponse:
      type: object
      required:
        - invoiceId
        - invoiceNumber
        - clientId
        - total
        - paymentTotal
        - openTotal
        - documentType
        - isCompleted
      properties:
        invoiceId:
          type: integer
        invoiceNumber:
          type: string
        clientId:
          type: integer
        total:
          type: number
        paymentTotal:
          type: number
        openTotal:
          type: number
        invoiceDate:
          type: string
          format: date-time
          nullable: true
        paymentDueDate:
          type: string
          format: date-time
          nullable: true
        paymentDate:
          type: string
          format: date-time
          nullable: true
        paymentComment:
          type: string
          nullable: true
        documentType:
          type: string
        isCompleted:
          type: boolean
    DutyResponse:
      type: object
      required:
        - dutyId
        - dutyName
        - dutyCode
        - dutyDate
        - prePlanned
        - planned
        - actual
        - employeeFirstName
        - employeeLastName
        - updatedBy
      properties:
        dutyId:
          type: integer
        dutyName:
          type: string
        dutyCode:
          type: string
        dutyDate:
          type: string
          format: date-time
        prePlanned:
          type: integer
        planned:
          type: integer
        actual:
          type: integer
        employeeFirstName:
          type: string
        employeeLastName:
          type: string
        employeeNumber:
          type: integer
          nullable: true
        updatedBy:
          type: string
        updatedAt:
          type: string
          format: date-time
          nullable: true
