openapi: 3.1.0
info:
  title: Integração PunchClock - Registro de Batida de Ponto
  version: 1.0.0
  description: |
    API para registro de batidas de ponto via integração externa.

    Esta API permite que sistemas externos registrem batidas de ponto (entrada, saída,
    início/fim de almoço) para colaboradores, integrando com o sistema de controle de
    jornada da Fhinck.

    ## Fluxo de Uso
    1. O sistema cliente envia uma requisição com os dados da batida de ponto
    2. A API valida a autenticação via `key`
    3. Os campos são validados (username, actionType, timestamp, etc.)
    4. O registro é inserido no BigQuery na tabela `time_tracking_{company}`
    5. Retorna confirmação com o ID único da ação registrada

    ## Autenticação
    - Via campo `key` no corpo da requisição (JSON)
    - A key é uma string criptografada que identifica a empresa

    ## Tipos de Ação (actionType)
    - `BEGIN_DAY` - Início do expediente (entrada)
    - `BEGIN_LUNCH` - Início do intervalo de almoço
    - `END_LUNCH` - Fim do intervalo de almoço
    - `END_DAY` - Fim do expediente (saída)

servers:
  - url: https://integrations.fhinck.com
    description: Produção
  - url: https://integrations-hml.fhinck.com
    description: Homologação

paths:
  /recordPunch:
    post:
      summary: Registrar batida de ponto
      description: |
        Registra uma batida de ponto individual para um colaborador.

        Cada chamada registra um único evento de ponto (entrada, saída ou intervalo).
        O timestamp do cliente é preservado em `action_datetime_sent` e o timestamp
        do servidor é registrado em `action_datetime_bq_insert`.
      operationId: integrationRecordPunch
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RecordPunchRequest'
            examples:
              entradaExpediente:
                summary: Registro de entrada (início do expediente)
                value:
                  key: 'SUA_CHAVE_DE_AUTENTICACAO'
                  username: 'michel.filho'
                  actionType: 'BEGIN_DAY'
                  actionDatetimeSent: '2026-01-12T09:00:00Z'
                  hostname: 'WORKSTATION-01'
                  locationType: 'office'
              inicioAlmoco:
                summary: Registro de início do almoço
                value:
                  key: 'SUA_CHAVE_DE_AUTENTICACAO'
                  username: 'michel.filho'
                  actionType: 'BEGIN_LUNCH'
                  actionDatetimeSent: '2026-01-12T12:00:00Z'
                  hostname: 'WORKSTATION-01'
                  locationType: 'office'
              fimAlmoco:
                summary: Registro de fim do almoço
                value:
                  key: 'SUA_CHAVE_DE_AUTENTICACAO'
                  username: 'michel.filho'
                  actionType: 'END_LUNCH'
                  actionDatetimeSent: '2026-01-12T13:00:00Z'
                  hostname: 'WORKSTATION-01'
                  locationType: 'office'
              saidaExpediente:
                summary: Registro de saída (fim do expediente)
                value:
                  key: 'SUA_CHAVE_DE_AUTENTICACAO'
                  username: 'michel.filho'
                  actionType: 'END_DAY'
                  actionDatetimeSent: '2026-01-12T18:00:00Z'
                  hostname: 'WORKSTATION-01'
                  locationType: 'office'
              homeOffice:
                summary: Registro em home office
                value:
                  key: 'SUA_CHAVE_DE_AUTENTICACAO'
                  username: 'joao.silva'
                  actionType: 'BEGIN_DAY'
                  actionDatetimeSent: '2026-01-12T08:30:00Z'
                  hostname: 'LAPTOP-HOME'
                  locationType: 'home'
                  source: 'mobile_app'
      responses:
        '200':
          description: Batida de ponto registrada com sucesso
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RecordPunchSuccessResponse'
              examples:
                sucesso:
                  value:
                    err: false
                    result:
                      success: true
                      action_id: '550e8400-e29b-41d4-a716-446655440000'
                      action_date: '2026-01-12'
                      action_type: 'BEGIN_DAY'
                      username: 'michel.filho'
        '400':
          description: Erro de autenticação
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RecordPunchErrorResponse'
              examples:
                keyAusente:
                  summary: Key não informada
                  value:
                    err: true
                    message: 'Parametro key não informado ou inválido'
                keyInvalida:
                  summary: Key inválida ou expirada
                  value:
                    err: true
                    message: 'Parametro key não informado ou inválido'
        '422':
          description: Erro de validação dos campos
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RecordPunchErrorResponse'
              examples:
                usernameAusente:
                  summary: Username não informado
                  value:
                    err: true
                    message: 'Parametro username é obrigatório e deve ser uma string não vazia'
                actionTypeInvalido:
                  summary: Tipo de ação inválido
                  value:
                    err: true
                    message: 'Parametro actionType deve ser: BEGIN_DAY, BEGIN_LUNCH, END_LUNCH ou END_DAY'
                timestampInvalido:
                  summary: Timestamp com formato incorreto
                  value:
                    err: true
                    message: 'Parametro actionDatetimeSent deve estar em formato ISO-8601 (ex: 2026-01-12T09:00:00Z)'
                hostnameAusente:
                  summary: Hostname não informado
                  value:
                    err: true
                    message: 'Parametro hostname é obrigatório e deve ser uma string não vazia'
                locationTypeAusente:
                  summary: Location type não informado
                  value:
                    err: true
                    message: 'Parametro locationType é obrigatório e deve ser uma string não vazia'
        '500':
          description: Erro interno do servidor
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InternalErrorResponse'
              examples:
                erroBigQuery:
                  value:
                    err: true
                    message: 'Erro ao registrar batida de ponto'

components:
  schemas:
    RecordPunchRequest:
      type: object
      required:
        - key
        - username
        - actionType
        - actionDatetimeSent
        - hostname
        - locationType
      properties:
        key:
          type: string
          description: |
            Chave única de autenticação do cliente (criptografada).
            Esta chave é fornecida pela Fhinck durante a integração.
          example: 'SUA_CHAVE_DE_AUTENTICACAO'
        username:
          type: string
          minLength: 1
          maxLength: 255
          description: |
            Identificador único do colaborador (login/username).
            Deve corresponder ao login cadastrado no sistema Fhinck.
          example: 'michel.filho'
        actionType:
          type: string
          enum:
            - BEGIN_DAY
            - BEGIN_LUNCH
            - END_LUNCH
            - END_DAY
          description: |
            Tipo de ação de ponto a ser registrada:
            - `BEGIN_DAY` - Início do expediente (entrada)
            - `BEGIN_LUNCH` - Início do intervalo de almoço
            - `END_LUNCH` - Fim do intervalo de almoço
            - `END_DAY` - Fim do expediente (saída)
          example: 'BEGIN_DAY'
        actionDatetimeSent:
          type: string
          format: date-time
          description: |
            Data e hora da batida de ponto no dispositivo do colaborador.
            Deve estar em formato ISO-8601 com timezone.
            Exemplos válidos:
            - `2026-01-12T09:00:00Z`
            - `2026-01-12T09:00:00.000Z`
            - `2026-01-12T06:00:00-03:00`
          example: '2026-01-12T09:00:00Z'
        hostname:
          type: string
          minLength: 1
          maxLength: 255
          description: |
            Nome do dispositivo/máquina onde a batida foi registrada.
            Usado para auditoria e rastreabilidade.
          example: 'WORKSTATION-01'
        locationType:
          type: string
          minLength: 1
          maxLength: 50
          description: |
            Tipo de localização onde a batida foi registrada.
            Valores sugeridos: `office`, `home`, `field`, `hybrid`
          example: 'office'
        source:
          type: string
          maxLength: 50
          description: |
            Origem do registro da batida (opcional).
            Se não informado, será definido como `integration`.
            Exemplos: `desktop_app`, `mobile_app`, `web`, `integration`
          default: 'integration'
          example: 'integration'

    RecordPunchSuccessResponse:
      type: object
      properties:
        err:
          type: boolean
          description: Indica se houve erro (false = sucesso)
          example: false
        result:
          type: object
          properties:
            success:
              type: boolean
              description: Confirmação de sucesso da operação
              example: true
            action_id:
              type: string
              format: uuid
              description: |
                Identificador único da batida de ponto registrada.
                Use este ID para rastrear ou consultar o registro.
              example: '550e8400-e29b-41d4-a716-446655440000'
            action_date:
              type: string
              format: date
              description: Data da batida de ponto (YYYY-MM-DD)
              example: '2026-01-12'
            action_type:
              type: string
              description: Tipo de ação registrada
              example: 'BEGIN_DAY'
            username:
              type: string
              description: Username do colaborador
              example: 'michel.filho'

    RecordPunchErrorResponse:
      type: object
      properties:
        err:
          type: boolean
          description: Indica se houve erro (true = erro)
          example: true
        message:
          type: string
          description: Mensagem descritiva do erro
          example: 'Parametro username é obrigatório e deve ser uma string não vazia'

    InternalErrorResponse:
      type: object
      properties:
        err:
          type: boolean
          example: true
        message:
          type: string
          example: 'Erro ao registrar batida de ponto'
