openapi: 3.0.3
info:
  title: TranscriMed Worklist API
  version: "1.0"
  description: |
    API para integração bidirecional com sistemas RIS/PACS para gerenciamento de listas de trabalho médicas.
    
    ## Funcionalidades
    - **Receber** listas de trabalho de sistemas externos
    - **Processar** e normalizar dados de exames
    - **Enviar** documentos médicos processados de volta aos sistemas de origem
    
    ## Como Funciona
    1. Seu sistema RIS/PACS envia a lista de trabalho via API
    2. Médicos visualizam e selecionam exames no TranscriMed
    3. Após processamento, o documento é enviado automaticamente de volta
    
    Baseado no padrão DICOM com campos em formato snake_case para melhor compatibilidade com APIs RESTful.
  contact:
    email: developers@transcrimed.com.br
  # x-logo is a vendor extension often used by ReDoc; it's fine, but Swagger Editor 3.0 accepts it as a free-form field
  x-logo:
    url: "/brand/logo-horizontal-notag.svg"
    altText: "TranscriMed"

servers:
  - url: "https://api.transcrimed.com.br"
    description: "Production server"

security:
  - OAuth2: [worklists:write, worklists:manage]

paths:
  /api/v1/worklists/ingest:
    post:
      tags:
        - Worklist Ingestion
      summary: Ingerir Itens de Lista de Trabalho
      description: |
        Recebe e processa itens de lista de trabalho de sistemas RIS/PACS externos.
        
        ## Características
        - Suporte para item único ou array de itens
        - Deduplicação automática baseada em `accession_number` ou `study_uid`
        - Idempotência via cabeçalho `Idempotency-Key`
        - Normalização de campos para padrão snake_case
        
        ## Campos Obrigatórios
        Pelo menos um dos seguintes campos deve estar presente:
        - `accession_number` - Número de acesso do exame
        - `study_uid` - UID único do estudo DICOM
      operationId: ingestWorklistItems
      security:
        - OAuth2: [worklists:write]
      parameters:
        - name: Idempotency-Key
          in: header
          description: Chave única para evitar processamento duplicado
          schema:
            type: string
            format: uuid
          example: "550e8400-e29b-41d4-a716-446655440000"
        - name: X-Source
          in: header
          description: Identificador opcional da fonte do sistema
          schema:
            type: string
          example: "hospital-sistema-ris"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - $ref: "#/components/schemas/WorklistItem"
                - type: array
                  items:
                    $ref: "#/components/schemas/WorklistItem"
                - type: object
                  properties:
                    items:
                      type: array
                      items:
                        $ref: "#/components/schemas/WorklistItem"
            examples:
              single_item:
                summary: Item único
                value:
                  accession_number: "2024-001234"
                  patient_id: "PAT-456789"
                  patient_name: "João Silva"
                  patient_sex: "M"
                  patient_birth_date: "1980-05-15"
                  modality: "CR"
                  exam_datetime: "2024-01-15T10:30:00Z"
                  exam_room: "Sala 1"
                  exam_description: "Raio-X de Tórax PA/Perfil"
                  study_uid: "1.2.826.0.1.3680043.6.15372.14625.20240115103000"
                  referring_physician: "Dr. Maria Santos"
                  hospital_name: "Hospital Central"
                  location: "Ala Norte"
              multiple_items:
                summary: Múltiplos itens
                value:
                  - accession_number: "2024-001234"
                    patient_id: "PAT-456789"
                    patient_name: "João Silva"
                    modality: "CR"
                    exam_datetime: "2024-01-15T10:30:00Z"
                    exam_description: "Raio-X de Tórax PA/Perfil"
                  - accession_number: "2024-001235"
                    patient_id: "PAT-456790"
                    patient_name: "Maria Santos"
                    modality: "CT"
                    exam_datetime: "2024-01-15T11:00:00Z"
                    exam_description: "Tomografia de Crânio"
              array_wrapper:
                summary: Com wrapper de array
                value:
                  items:
                    - accession_number: "2024-001234"
                      patient_id: "PAT-456789"
                      patient_name: "João Silva"
                      modality: "CR"
                      exam_datetime: "2024-01-15T10:30:00Z"
                      exam_description: "Raio-X de Tórax PA/Perfil"
      responses:
        "200":
          description: Itens processados com sucesso
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/IngestResponse"
              examples:
                success:
                  summary: Processamento bem-sucedido
                  value:
                    success: true
                    data:
                      batch_id: "batch_12345-67890"
                      inserted: 2
                      updated: 1
                      deduped: 0
                      errors: []
                    meta:
                      request_id: "req_abc123"
                      timestamp: "2024-01-15T10:30:00Z"
                partial_success:
                  summary: Processamento parcial com erros
                  value:
                    success: true
                    data:
                      batch_id: "batch_12345-67890"
                      inserted: 1
                      updated: 0
                      deduped: 1
                      errors:
                        - index: 2
                          error: "Missing required field: accession_number or study_uid"
                    meta:
                      request_id: "req_abc123"
                      timestamp: "2024-01-15T10:30:00Z"
        "400":
          description: Erro de validação
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              examples:
                validation_error:
                  summary: Erro de validação
                  value:
                    success: false
                    error:
                      code: "VALIDATION_ERROR"
                      message: "Request validation failed"
                      details:
                        errors:
                          - "Item 0: Missing required field: accession_number or study_uid"
                          - "Item 1: Invalid modality code"
                    meta:
                      request_id: "req_abc123"
                      timestamp: "2024-01-15T10:30:00Z"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "429":
          $ref: "#/components/responses/RateLimited"

  /api/v1/worklists/items/{id}:
    patch:
      tags:
        - Worklist Management
      summary: Atualizar Status de Item
      description: |
        Atualiza o status de um item específico da lista de trabalho.
        
        ## Estados Disponíveis
        - `released` - Documento liberado (padrão)
        - `cancelled` - Cancelado

        Compatibilidade: `completed` é aceito e convertido para `released`.
        
        Usado principalmente para sistemas externos notificarem mudanças de status.
      operationId: updateWorklistItem
      security:
        - OAuth2: [worklists:manage]
      parameters:
        - name: id
          in: path
          required: true
          description: ID único do item da lista de trabalho
          schema:
            type: string
            format: uuid
          example: "550e8400-e29b-41d4-a716-446655440000"
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateWorklistItemRequest"
            examples:
              update_status:
                summary: Atualizar status
                value:
                  status: "in_progress"
                  reason: "Exame em andamento na sala 1"
              cancel_exam:
                summary: Cancelar exame
                value:
                  status: "cancelled"
                  reason: "Paciente não compareceu"
      responses:
        "200":
          description: Item atualizado com sucesso
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WorklistItemResponse"
              example:
                success: true
                data:
                  id: "550e8400-e29b-41d4-a716-446655440000"
                  status: "in_progress"
                  updated_at: "2024-01-15T10:35:00Z"
                  accession_number: "2024-001234"
                  patient_name: "João Silva"
                  exam_description: "Raio-X de Tórax PA/Perfil"
                meta:
                  request_id: "req_abc123"
                  timestamp: "2024-01-15T10:35:00Z"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
        "403":
          $ref: "#/components/responses/Forbidden"
        "404":
          $ref: "#/components/responses/NotFound"

components:
  securitySchemes:
    OAuth2:
      type: oauth2
      flows:
        authorizationCode:
          authorizationUrl: /api/oauth/authorize
          tokenUrl: /api/oauth/token
          refreshUrl: /api/oauth/token
          scopes:
            worklists:write: Enviar itens de lista de trabalho
            worklists:manage: Gerenciar status de itens
            worklists:read: Ler itens de lista de trabalho

  schemas:
    WorklistItem:
      type: object
      description: Item individual de lista de trabalho baseado no padrão DICOM
      properties:
        # Identificadores principais (pelo menos um obrigatório)
        accession_number:
          type: string
          maxLength: 100
          description: Número de acesso único do exame
          example: "2024-001234"
        study_uid:
          type: string
          maxLength: 200
          description: UID único do estudo DICOM
          example: "1.2.826.0.1.3680043.6.15372.14625.20240115103000"
        
        # Dados do paciente
        patient_id:
          type: string
          maxLength: 50
          description: Identificador único do paciente
          example: "PAT-456789"
        patient_name:
          type: string
          maxLength: 200
          description: Nome completo do paciente
          example: "João Silva"
        patient_sex:
          type: string
          enum: [M, F, O, U, Male, Female, MASCULINO, FEMININO]
          description: Sexo do paciente (aceita múltiplos formatos)
          example: "M"
        patient_birth_date:
          type: string
          format: date
          description: Data de nascimento (ISO 8601)
          example: "1980-05-15"
        patient_age:
          type: string
          maxLength: 20
          description: Idade do paciente (formato livre)
          example: "44Y"
        
        # Dados do exame
        modality:
          type: string
          maxLength: 10
          description: Modalidade do exame (CR, CT, MR, US, etc.)
          example: "CR"
        exam_datetime:
          type: string
          format: date-time
          description: Data e hora do exame (ISO 8601)
          example: "2024-01-15T10:30:00Z"
        exam_room:
          type: string
          maxLength: 100
          description: Sala onde o exame será/foi realizado
          example: "Sala 1"
        exam_description:
          type: string
          maxLength: 500
          description: Descrição do procedimento/exame
          example: "Raio-X de Tórax PA/Perfil"
        
        # Dados adicionais
        referring_physician:
          type: string
          maxLength: 200
          description: Médico solicitante
          example: "Dr. Maria Santos"
        procedure_id:
          type: string
          maxLength: 50
          description: ID do procedimento
          example: "PROC-12345"
        procedure_step_id:
          type: string
          maxLength: 50
          description: ID do passo do procedimento
          example: "STEP-67890"
        hospital_name:
          type: string
          maxLength: 200
          description: Nome da instituição/hospital
          example: "Hospital Central"
        location:
          type: string
          maxLength: 200
          description: Localização/setor dentro da instituição
          example: "Ala Norte - Radiologia"
        
        # Metadados adicionais
        metadata:
          type: object
          description: Campos adicionais específicos do sistema
          additionalProperties: true
      
      # Validação: pelo menos um identificador deve estar presente
      anyOf:
        - required: [accession_number]
        - required: [study_uid]
      
      example:
        accession_number: "2024-001234"
        patient_id: "PAT-456789"
        patient_name: "João Silva"
        patient_sex: "M"
        patient_birth_date: "1980-05-15"
        modality: "CR"
        exam_datetime: "2024-01-15T10:30:00Z"
        exam_room: "Sala 1"
        exam_description: "Raio-X de Tórax PA/Perfil"
        study_uid: "1.2.826.0.1.3680043.6.15372.14625.20240115103000"
        referring_physician: "Dr. Maria Santos"
        hospital_name: "Hospital Central"
        location: "Ala Norte"

    UpdateWorklistItemRequest:
      type: object
      description: Requisição para atualizar status de item
      properties:
        status:
          type: string
          enum: [new, in_progress, completed, cancelled, archived]
          description: Novo status do item
        reason:
          type: string
          maxLength: 500
          description: Motivo da alteração (opcional)
      required:
        - status
      example:
        status: "in_progress"
        reason: "Exame em andamento na sala 1"

    IngestResponse:
      type: object
      description: Resposta do processamento de ingestão
      properties:
        success:
          type: boolean
          example: true
        data:
          type: object
          properties:
            batch_id:
              type: string
              description: ID do lote processado
              example: "batch_12345-67890"
            inserted:
              type: integer
              description: Número de itens inseridos
              example: 2
            updated:
              type: integer
              description: Número de itens atualizados
              example: 1
            deduped:
              type: integer
              description: Número de itens duplicados ignorados
              example: 0
            errors:
              type: array
              description: Lista de erros por item
              items:
                type: object
                properties:
                  index:
                    type: integer
                    description: Índice do item com erro
                  error:
                    type: string
                    description: Descrição do erro
        meta:
          $ref: "#/components/schemas/ResponseMeta"

    WorklistItemResponse:
      type: object
      properties:
        success:
          type: boolean
          example: true
        data:
          type: object
          properties:
            id:
              type: string
              format: uuid
              description: ID único do item
            status:
              type: string
              description: Status atual do item
            updated_at:
              type: string
              format: date-time
              description: Timestamp da última atualização
            accession_number:
              type: string
              description: Número de acesso
            patient_name:
              type: string
              description: Nome do paciente
            exam_description:
              type: string
              description: Descrição do exame
        meta:
          $ref: "#/components/schemas/ResponseMeta"

    ErrorResponse:
      type: object
      description: Resposta padrão de erro
      properties:
        success:
          type: boolean
          example: false
        error:
          type: object
          properties:
            code:
              type: string
              description: Código do erro
              example: "VALIDATION_ERROR"
            message:
              type: string
              description: Mensagem de erro legível
              example: "Request validation failed"
            details:
              type: object
              description: Detalhes adicionais do erro
              additionalProperties: true
        meta:
          $ref: "#/components/schemas/ResponseMeta"

    ResponseMeta:
      type: object
      description: Metadados da resposta
      properties:
        request_id:
          type: string
          description: ID único da requisição para rastreamento
          example: "req_abc123"
        timestamp:
          type: string
          format: date-time
          description: Timestamp da resposta
          example: "2024-01-15T10:30:00Z"
        user_id:
          type: string
          description: ID do usuário (quando aplicável)
          example: "user_xyz789"
        client_id:
          type: string
          description: ID do cliente OAuth2
          example: "client_def456"

  responses:
    BadRequest:
      description: Requisição inválida
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            success: false
            error:
              code: "BAD_REQUEST"
              message: "Invalid request parameters"
            meta:
              request_id: "req_abc123"
              timestamp: "2024-01-15T10:30:00Z"

    Unauthorized:
      description: Token de acesso ausente ou inválido
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            success: false
            error:
              code: "UNAUTHORIZED"
              message: "Authentication required"
            meta:
              request_id: "req_abc123"
              timestamp: "2024-01-15T10:30:00Z"

    Forbidden:
      description: Acesso negado - escopos insuficientes
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            success: false
            error:
              code: "FORBIDDEN"
              message: "Insufficient permissions - requires scope: worklists:write"
            meta:
              request_id: "req_abc123"
              timestamp: "2024-01-15T10:30:00Z"

    NotFound:
      description: Recurso não encontrado
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            success: false
            error:
              code: "NOT_FOUND"
              message: "Worklist item not found"
            meta:
              request_id: "req_abc123"
              timestamp: "2024-01-15T10:30:00Z"

    RateLimited:
      description: Limite de taxa excedido
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/ErrorResponse"
          example:
            success: false
            error:
              code: "RATE_LIMIT_EXCEEDED"
              message: "Too many requests - retry after 60 seconds"
            meta:
              request_id: "req_abc123"
              timestamp: "2024-01-15T10:30:00Z"
      headers:
        Retry-After:
          description: Seconds to wait before retrying
          schema:
            type: integer
            example: 60

tags:
  - name: Worklist Ingestion
    description: Endpoints para receber listas de trabalho de sistemas externos
  - name: Worklist Management
    description: Endpoints para gerenciar status e estado de itens