openapi: 3.0.3
info:
  title: Prova Fácil - API de Integração com Parceiros
  version: "1.0.13"
  description: |
    API REST (JSON) para integração de parceiros com a plataforma de gestão de
    avaliação da Starline (Prova Fácil). Migrada da documentação original em
    API Blueprint (Apiary) para OpenAPI 3.0.

    ## Autenticação
    Autentique via `POST /api/v1/rest/auth_token/` com `username`/`password`.
    O token retornado deve ser enviado em todas as chamadas subsequentes no
    header `Authorization: Token <token>` (nota o espaço obrigatório entre
    "Token" e a chave).

    ## Escopo (hierarquia Cliente -> Grupo Empresarial -> Polo)
    A maioria dos serviços aceita os query params `client_group` e
    `client_unit` para definir o nível de escopo da chamada:
    - Nível Cliente: não informar nenhum dos dois (definido pela URL/subdomínio).
    - Nível Grupo Empresarial: informar apenas `client_group`.
    - Nível Polo: informar `client_group` e `client_unit`.

    ## Filtros globais
    Campos comuns podem ser filtrados via query string, entre eles:
    `client_slug`, `client_group`, `client_unit`, `client_unit_null`,
    `last_update`, `last_update_days_ago`, `legacy_key`, `name__startswith`,
    `name__endswith`, `name__contains`, `status`.
  contact:
    name: Starline Tecnologia
servers:
  - url: "https://{cliente}.provafacilnaweb.com.br/{cliente}"
    description: Ambiente do cliente (multi-tenant por subdomínio e path)
    variables:
      cliente:
        default: nomecliente
        description: Slug do cliente/instituição

security:
  - TokenAuth: []

tags:
  - name: Autenticação
  - name: ClientGroup
  - name: ClientUnit
  - name: User
  - name: PlaceConfig
  - name: Place
  - name: ContentConfig
  - name: Content
  - name: Candidate
  - name: CandidateLogin
  - name: ContentXCandidate
  - name: QuestionXContent
  - name: ContentXUser
  - name: Regulation
  - name: Instruction
  - name: AcademicConfig
  - name: Academic
  - name: AcademicXOwners
  - name: AcademicXCandidate
  - name: TemplateTest
  - name: TypeAssessment
  - name: GroupAssessment
  - name: ScheduleSetup
  - name: Schedule
  - name: CandidateWaitingPlace
  - name: ScheduleCandidatePlace
  - name: GroupAssessmentSchedules
  - name: ScheduleAllCandidates
  - name: ScheduleCandidate
  - name: ScheduleNotNominal
  - name: BatchSchedule
  - name: ScoreXCandidateXContent
  - name: WorkflowStep
  - name: WorkflowUserXContent
  - name: Period
  - name: APIs V2

components:
  securitySchemes:
    TokenAuth:
      type: apiKey
      in: header
      name: Authorization
      description: 'Formato: `Token <chave>` (espaço obrigatório).'

  parameters:
    ClientGroup:
      name: client_group
      in: query
      schema: { type: string }
      description: Código do Grupo Empresarial (escopo).
    ClientUnit:
      name: client_unit
      in: query
      schema: { type: string }
      description: Código da Unidade/Polo (escopo).
    LegacyKey:
      name: legacy_key
      in: query
      schema: { type: string }
      description: Filtra pela chave gerada pelo parceiro.
    LastUpdate:
      name: last_update
      in: query
      schema: { type: string, format: date }
      description: Filtra por data de atualização (YYYY-MM-DD).
    NameStartsWith:
      name: name__startswith
      in: query
      schema: { type: string }
    KeyPath:
      name: key
      in: path
      required: true
      schema: { type: string }
      description: Chave única gerada pelo SGP.

  responses:
    NoContent:
      description: Excluído com sucesso.
    OK:
      description: Sucesso.

  schemas:
    Error:
      type: object
      properties:
        detail:
          type: string

  examples: {}

paths:
  /api/v1/rest/auth_token/:
    post:
      tags: [Autenticação]
      summary: Autenticar e obter token
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [username, password]
              properties:
                username: { type: string }
                password: { type: string }
      responses:
        "200":
          description: Token de autenticação
          content:
            application/json:
              schema:
                type: object
                properties:
                  token: { type: string, example: "62d437c8574a8321231231c72d321da9094d" }

  /api/v1/a/rest/api/clientgroup/:
    get:
      tags: [ClientGroup]
      summary: Listar client groups
      parameters:
        - $ref: '#/components/parameters/LegacyKey'
      responses:
        "200": { description: Lista de client groups }
    post:
      tags: [ClientGroup]
      summary: Criar client group
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                client: { type: string }
                name: { type: string }
      responses:
        "200": { description: Client group criado }

  /api/v1/a/rest/api/clientgroup/{key}/:
    get:
      tags: [ClientGroup]
      summary: Obter client group por key
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    put:
      tags: [ClientGroup]
      summary: Atualizar (todos os campos) client group
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }

  /api/v1/a/rest/api/clientunit/:
    get:
      tags: [ClientUnit]
      summary: Listar client units
      parameters:
        - $ref: '#/components/parameters/LegacyKey'
      responses: { "200": { description: OK } }
    post:
      tags: [ClientUnit]
      summary: Criar client unit
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                legacy_key: { type: string }
                client_group: { type: string }
                name: { type: string }
      responses: { "200": { description: OK } }

  /api/v1/a/rest/api/clientunit/{key}/:
    get:
      tags: [ClientUnit]
      summary: Obter client unit por key
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    put:
      tags: [ClientUnit]
      summary: Atualizar client unit
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }

  /api/v1/a/rest/user/:
    get:
      tags: [User]
      summary: Listar usuários (ou obter um específico via user_name)
      parameters:
        - name: user_name
          in: query
          schema: { type: string }
          description: Filtra e retorna apenas o usuário com este username.
      responses: { "200": { description: OK } }



  /api/v1/a/rest/user/post/:
    post:
      tags: [User]
      summary: Criar usuário
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                profile:
                  type: object
                  properties:
                    type: { type: integer, description: "1 admin, 2 coord, 3 secretary, 4 teacher" }
                    client_group: { type: string }
                    units: { type: array, items: { type: string } }
                password: { type: string }
                username: { type: string }
                first_name: { type: string }
                last_name: { type: string }
                email: { type: string }
                is_active: { type: boolean }
                groups: { type: array, items: { type: integer } }
      responses: { "200": { description: OK } }

  /api/v1/a/rest/user/{key}/:
    get:
      tags: [User]
      summary: Obter usuário por id
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    put:
      tags: [User]
      summary: Atualizar usuário (todos os campos)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    patch:
      tags: [User]
      summary: Atualizar usuário (campos parciais)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    delete:
      tags: [User]
      summary: Excluir usuário
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "204": { $ref: '#/components/responses/NoContent' } }

  /api/v1/tm/rest/placeconfig/:
    get:
      tags: [PlaceConfig]
      summary: Listar configurações de local de aplicação
      parameters:
        - $ref: '#/components/parameters/LegacyKey'
      responses: { "200": { description: OK } }

  /api/v1/tm/rest/placeconfig/{key}/:
    get:
      tags: [PlaceConfig]
      summary: Obter configuração de local por key
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }

  /api/v1/tm/rest/place/:
    get:
      tags: [Place]
      summary: Listar locais de aplicação
      parameters:
        - name: placing_level
          in: query
          schema: { type: boolean }
          description: Filtra somente locais disponíveis para ensalamento.
        - name: place_config
          in: query
          schema: { type: string }
        - name: parent
          in: query
          schema: { type: string }
        - $ref: '#/components/parameters/LegacyKey'
      responses: { "200": { description: OK } }
    post:
      tags: [Place]
      summary: Criar local de aplicação
      responses: { "200": { description: OK } }

  /api/v1/tm/rest/place/{key}/:
    get:
      tags: [Place]
      summary: Obter local por key
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    put:
      tags: [Place]
      summary: Atualizar local (todos os campos)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    patch:
      tags: [Place]
      summary: Atualizar local (campos parciais)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    delete:
      tags: [Place]
      summary: Excluir local
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "204": { $ref: '#/components/responses/NoContent' } }

  /api/v1/str/rest/api/config/content/:
    get:
      tags: [ContentConfig]
      summary: Listar configurações de conteúdo
      responses: { "200": { description: OK } }

  /api/v1/str/rest/api/config/content/{key}/:
    get:
      tags: [ContentConfig]
      summary: Obter configuração de conteúdo por key
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }

  /api/v1/str/rest/api/content/:
    get:
      tags: [Content]
      summary: Listar itens da estrutura de conteúdo
      parameters:
        - name: order
          in: query
          schema: { type: integer }
        - name: parent
          in: query
          schema: { type: string }
        - $ref: '#/components/parameters/LegacyKey'
      responses: { "200": { description: OK } }
    post:
      tags: [Content]
      summary: Criar item de conteúdo
      responses: { "200": { description: OK } }

  /api/v1/str/rest/api/content/{key}/:
    get:
      tags: [Content]
      summary: Obter conteúdo por key
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    put:
      tags: [Content]
      summary: Atualizar conteúdo (todos os campos)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    patch:
      tags: [Content]
      summary: Atualizar conteúdo (campos parciais)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    delete:
      tags: [Content]
      summary: Excluir conteúdo
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "204": { $ref: '#/components/responses/NoContent' } }

  /api/v1/tm/rest/candidate/:
    get:
      tags: [Candidate]
      summary: Listar candidatos
      parameters:
        - $ref: '#/components/parameters/LegacyKey'
        - $ref: '#/components/parameters/LastUpdate'
        - name: name_startswith
          in: query
          schema: { type: string }
      responses: { "200": { description: OK } }
    post:
      tags: [Candidate]
      summary: Criar candidato
      responses: { "200": { description: OK } }

  /api/v1/tm/rest/candidate/{key}/:
    get:
      tags: [Candidate]
      summary: Obter candidato por key
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    put:
      tags: [Candidate]
      summary: Atualizar candidato (todos os campos)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    patch:
      tags: [Candidate]
      summary: Atualizar candidato (campos parciais)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    delete:
      tags: [Candidate]
      summary: Excluir candidato
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "204": { $ref: '#/components/responses/NoContent' } }

  /api/v1/tm/rest/candidategenerate/:
    post:
      tags: [Candidate]
      summary: Criar candidato em um agendamento e gerar sua prova
      responses: { "200": { description: OK } }

  /api/v1/tm/rest/candidatelogin/:
    get:
      tags: [CandidateLogin]
      summary: Listar candidate logins
      parameters:
        - name: login
          in: query
          schema: { type: string }
      responses: { "200": { description: OK } }
    post:
      tags: [CandidateLogin]
      summary: Criar candidate login
      responses: { "200": { description: OK } }

  /api/v1/tm/rest/candidatelogin/{key}/:
    get:
      tags: [CandidateLogin]
      summary: Obter candidate login por key
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    put:
      tags: [CandidateLogin]
      summary: Atualizar candidate login
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    patch:
      tags: [CandidateLogin]
      summary: Atualizar candidate login (parcial)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    delete:
      tags: [CandidateLogin]
      summary: Excluir candidate login
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "204": { $ref: '#/components/responses/NoContent' } }

  /api/v1/str/rest/api/contentxcandidate/:
    get:
      tags: [ContentXCandidate]
      summary: Listar relações Content x Candidate
      parameters:
        - name: candidate
          in: query
          schema: { type: string }
        - name: content
          in: query
          schema: { type: string }
        - name: period
          in: query
          schema: { type: string }
        - name: type_assessment
          in: query
          schema: { type: string }
      responses: { "200": { description: OK } }
    post:
      tags: [ContentXCandidate]
      summary: Criar relação Content x Candidate
      responses: { "200": { description: OK } }

  /api/v1/str/rest/api/contentxcandidate/{key}/:
    get:
      tags: [ContentXCandidate]
      summary: Obter relação por key
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    put:
      tags: [ContentXCandidate]
      summary: Atualizar relação (todos os campos)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    patch:
      tags: [ContentXCandidate]
      summary: Atualizar relação (parcial)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    delete:
      tags: [ContentXCandidate]
      summary: Excluir relação
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "204": { $ref: '#/components/responses/NoContent' } }

  /api/v1/str/rest/api/questioncontents/{schedule_key}/:
    get:
      tags: [QuestionXContent]
      summary: Listar questões e conteúdos vinculados a um agendamento
      parameters:
        - name: schedule_key
          in: path
          required: true
          schema: { type: string }
      responses: { "200": { description: OK } }

  /api/v1/str/rest/api/contentxuser/{key}/:
    get:
      tags: [ContentXUser]
      summary: Listar donos (owners) de um conteúdo
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    post:
      tags: [ContentXUser]
      summary: Vincular usuários como donos do conteúdo
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    delete:
      tags: [ContentXUser]
      summary: Remover usuários como donos do conteúdo
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }

  /api/v1/tpl/rest/regulation/:
    get:
      tags: [Regulation]
      summary: Listar regras de modelo de prova
      parameters:
        - $ref: '#/components/parameters/LegacyKey'
      responses: { "200": { description: OK } }

  /api/v1/tpl/rest/regulation/{key}/:
    get:
      tags: [Regulation]
      summary: Obter regra por key
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }

  /api/v1/tm/rest/instruction/:
    get:
      tags: [Instruction]
      summary: Listar instruções
      responses: { "200": { description: OK } }

  /api/v1/tm/rest/instruction/{key}/:
    get:
      tags: [Instruction]
      summary: Obter instrução por key
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }

  /api/v1/str/rest/api/config/academic/:
    get:
      tags: [AcademicConfig]
      summary: Listar configurações de acadêmico
      responses: { "200": { description: OK } }

  /api/v1/str/rest/api/config/academic/{key}/:
    get:
      tags: [AcademicConfig]
      summary: Obter configuração de acadêmico por key
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }

  /api/v1/str/rest/api/academic/:
    get:
      tags: [Academic]
      summary: Listar estrutura acadêmica
      parameters:
        - name: order
          in: query
          schema: { type: integer }
        - name: parent
          in: query
          schema: { type: string }
        - $ref: '#/components/parameters/LegacyKey'
      responses: { "200": { description: OK } }
    post:
      tags: [Academic]
      summary: Criar item da estrutura acadêmica
      responses: { "200": { description: OK } }

  /api/v1/str/rest/api/academic/{key}/:
    get:
      tags: [Academic]
      summary: Obter acadêmico por key
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    put:
      tags: [Academic]
      summary: Atualizar acadêmico (todos os campos)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    patch:
      tags: [Academic]
      summary: Atualizar acadêmico (parcial)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    delete:
      tags: [Academic]
      summary: Excluir acadêmico
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "204": { $ref: '#/components/responses/NoContent' } }

  /api/v1/str/rest/academicsowners/:
    post:
      tags: [AcademicXOwners]
      summary: Inserir donos na estrutura acadêmica
      responses: { "200": { description: OK } }
    delete:
      tags: [AcademicXOwners]
      summary: Excluir donos da estrutura acadêmica
      responses: { "204": { $ref: '#/components/responses/NoContent' } }

  /api/v1/str/rest/api/academicxcandidate/:
    get:
      tags: [AcademicXCandidate]
      summary: Listar relações Academic x Candidate
      parameters:
        - name: candidate
          in: query
          schema: { type: string }
        - name: academic
          in: query
          schema: { type: string }
        - name: name_startswith
          in: query
          schema: { type: string }
        - $ref: '#/components/parameters/LegacyKey'
        - $ref: '#/components/parameters/LastUpdate'
      responses: { "200": { description: OK } }
    post:
      tags: [AcademicXCandidate]
      summary: Criar relação Academic x Candidate
      responses: { "200": { description: OK } }

  /api/v1/str/rest/api/academicxcandidate/{id}/:
    get:
      tags: [AcademicXCandidate]
      summary: Obter relação por id
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses: { "200": { description: OK } }
    put:
      tags: [AcademicXCandidate]
      summary: Atualizar relação (todos os campos)
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses: { "200": { description: OK } }
    patch:
      tags: [AcademicXCandidate]
      summary: Atualizar relação (parcial)
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses: { "200": { description: OK } }
    delete:
      tags: [AcademicXCandidate]
      summary: Excluir relação
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: integer }
      responses: { "204": { $ref: '#/components/responses/NoContent' } }

  /api/v1/tpl/rest/templatetest/:
    get:
      tags: [TemplateTest]
      summary: Listar modelos de prova
      parameters:
        - name: academic_key
          in: query
          schema: { type: string }
        - name: type_template
          in: query
          schema: { type: string, description: "D-Dinâmico, S-Estático, G-Gabarito, Q-Questionário" }
        - name: status_template
          in: query
          schema: { type: string, description: "O-Aberto, C-Completo" }
        - name: code
          in: query
          schema: { type: integer }
        - name: name__contains
          in: query
          schema: { type: string }
        - name: paginate
          in: query
          schema: { type: string }
      responses: { "200": { description: OK } }

  /api/v1/tpl/rest/templatetest/{key}/:
    get:
      tags: [TemplateTest]
      summary: Obter modelo de prova por key
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }

  /api/v1/tpl/rest/templatetestdynamic/:
    get:
      tags: [TemplateTest]
      summary: Listar modelos de prova dinâmicos
      parameters:
        - name: academic_key
          in: query
          schema: { type: string }
        - name: status_template
          in: query
          schema: { type: string }
        - name: content_key
          in: query
          schema: { type: string }
      responses: { "200": { description: OK } }
    post:
      tags: [TemplateTest]
      summary: Criar modelo de prova dinâmico
      responses: { "200": { description: OK } }

  /api/v1/tpl/rest/templatetestdynamic/{key}/:
    get:
      tags: [TemplateTest]
      summary: Obter modelo dinâmico por key
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    put:
      tags: [TemplateTest]
      summary: Atualizar modelo dinâmico (todos os campos)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    patch:
      tags: [TemplateTest]
      summary: Atualizar modelo dinâmico (parcial)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    delete:
      tags: [TemplateTest]
      summary: Excluir modelo dinâmico
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "204": { $ref: '#/components/responses/NoContent' } }

  /api/v1/tpl/rest/templateteststatic/:
    get:
      tags: [TemplateTest]
      summary: Listar modelos de prova estáticos
      parameters:
        - name: academic_key
          in: query
          schema: { type: string }
        - name: status_template
          in: query
          schema: { type: string }
      responses: { "200": { description: OK } }
    post:
      tags: [TemplateTest]
      summary: Criar modelo de prova estático
      responses: { "200": { description: OK } }

  /api/v1/tpl/rest/templateteststatic/{key}/:
    get:
      tags: [TemplateTest]
      summary: Obter modelo estático por key
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    put:
      tags: [TemplateTest]
      summary: Atualizar modelo estático (todos os campos)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    patch:
      tags: [TemplateTest]
      summary: Atualizar modelo estático (parcial)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    delete:
      tags: [TemplateTest]
      summary: Excluir modelo estático
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "204": { $ref: '#/components/responses/NoContent' } }

  /api/v1/s/rest/typeassessment/:
    get:
      tags: [TypeAssessment]
      summary: Listar tipos de avaliação
      parameters:
        - $ref: '#/components/parameters/LegacyKey'
      responses: { "200": { description: OK } }
    post:
      tags: [TypeAssessment]
      summary: Criar tipo de avaliação
      responses: { "200": { description: OK } }

  /api/v1/s/rest/typeassessment/{key}/:
    get:
      tags: [TypeAssessment]
      summary: Obter tipo de avaliação por key
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    put:
      tags: [TypeAssessment]
      summary: Atualizar tipo de avaliação
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    patch:
      tags: [TypeAssessment]
      summary: Atualizar tipo de avaliação (parcial)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    delete:
      tags: [TypeAssessment]
      summary: Excluir tipo de avaliação
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "204": { $ref: '#/components/responses/NoContent' } }

  /api/v1/s/rest/groupassessment/:
    get:
      tags: [GroupAssessment]
      summary: Listar grupos de agendamento
      parameters:
        - $ref: '#/components/parameters/LegacyKey'
      responses: { "200": { description: OK } }
    post:
      tags: [GroupAssessment]
      summary: Criar grupo de agendamento
      responses: { "200": { description: OK } }

  /api/v1/s/rest/groupassessment/{key}/:
    get:
      tags: [GroupAssessment]
      summary: Obter grupo de agendamento por key
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    put:
      tags: [GroupAssessment]
      summary: Atualizar grupo de agendamento
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    patch:
      tags: [GroupAssessment]
      summary: Atualizar grupo de agendamento (parcial)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    delete:
      tags: [GroupAssessment]
      summary: Excluir grupo de agendamento
      description: |
        Por padrão não é possível excluir um grupo que contenha agendamento
        parcialmente gerado, gerado, processado, parcialmente processado ou
        finalizado, a menos que `forced=true` seja usado (o que apaga também
        os documentos relacionados).
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "204": { $ref: '#/components/responses/NoContent' } }

  /api/v1/s/rest/schedulesetup/:
    get:
      tags: [ScheduleSetup]
      summary: Listar configurações de agendamento
      parameters:
        - name: academic
          in: query
          schema: { type: string }
        - name: assessment_type
          in: query
          schema: { type: string }
        - name: status
          in: query
          schema: { type: integer }
      responses: { "200": { description: OK } }

  /api/v1/s/rest/schedule/:
    get:
      tags: [Schedule]
      summary: Listar agendamentos
      parameters:
        - name: groupassessment
          in: query
          schema: { type: string }
        - name: academic
          in: query
          schema: { type: string }
        - name: type_assessment
          in: query
          schema: { type: string }
        - name: date_schedule_initial
          in: query
          schema: { type: string, format: date }
        - name: date_schedule_final
          in: query
          schema: { type: string, format: date }
        - name: template_code
          in: query
          schema: { type: string }
        - name: template_legacy_key
          in: query
          schema: { type: string }
        - name: template_null
          in: query
          schema: { type: boolean }
        - $ref: '#/components/parameters/LegacyKey'
      responses: { "200": { description: OK } }
    post:
      tags: [Schedule]
      summary: Criar agendamento
      responses: { "200": { description: OK } }

  /api/v1/s/rest/schedulewithperiod/:
    post:
      tags: [Schedule]
      summary: Criar agendamento (ambientes com conceito de período)
      responses: { "200": { description: OK } }

  /api/v1/s/rest/schedule/{key}/:
    get:
      tags: [Schedule]
      summary: Obter agendamento por key
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    put:
      tags: [Schedule]
      summary: Atualizar agendamento (todos os campos)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    patch:
      tags: [Schedule]
      summary: Atualizar agendamento (parcial)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    delete:
      tags: [Schedule]
      summary: Excluir agendamento
      description: |
        Requer `forced=true` se o agendamento já estiver parcialmente
        gerado/gerado/processado/finalizado (apaga documentos relacionados).
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "204": { $ref: '#/components/responses/NoContent' } }

  /api/v1/s/rest/candidateswaitingplace/:
    get:
      tags: [CandidateWaitingPlace]
      summary: Listar candidatos aguardando distribuição por sala
      parameters:
        - name: schedule
          in: query
          schema: { type: string }
        - name: candidate
          in: query
          schema: { type: string }
        - name: place
          in: query
          schema: { type: string }
      responses: { "200": { description: OK } }
    post:
      tags: [CandidateWaitingPlace]
      summary: Inserir candidato para distribuição por sala
      responses: { "200": { description: OK } }

  /api/v1/s/rest/candidateswaitingplace/{key}/:
    get:
      tags: [CandidateWaitingPlace]
      summary: Obter por key
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    put:
      tags: [CandidateWaitingPlace]
      summary: Atualizar (todos os campos)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    patch:
      tags: [CandidateWaitingPlace]
      summary: Atualizar (parcial)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    delete:
      tags: [CandidateWaitingPlace]
      summary: Excluir
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "204": { $ref: '#/components/responses/NoContent' } }

  /api/v1/s/rest/schedulecandidatexplace/:
    get:
      tags: [ScheduleCandidatePlace]
      summary: Listar Schedule Candidate Place
      parameters:
        - name: attendance
          in: query
          schema: { type: boolean }
        - name: candidate
          in: query
          schema: { type: string }
        - name: date_schedule__lte
          in: query
          schema: { type: string, format: date }
        - name: date_schedule__gte
          in: query
          schema: { type: string, format: date }
        - name: schedule
          in: query
          schema: { type: string }
        - name: place
          in: query
          schema: { type: string }
        - name: status_item_generated_id
          in: query
          schema: { type: string }
        - name: status_item_generated_id__gte
          in: query
          schema: { type: string }
        - name: type_assessment
          in: query
          schema: { type: string }
        - name: paginate
          in: query
          schema: { type: string }
      responses: { "200": { description: "Lista paginada (count/next/previous/results)" } }
    post:
      tags: [ScheduleCandidatePlace]
      summary: Criar Schedule Candidate Place
      responses: { "200": { description: OK } }

  /api/v1/s/rest/schedulecandidatexplace/{key}/:
    get:
      tags: [ScheduleCandidatePlace]
      summary: Obter por key
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    put:
      tags: [ScheduleCandidatePlace]
      summary: Atualizar (todos os campos)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    patch:
      tags: [ScheduleCandidatePlace]
      summary: Atualizar (parcial)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    delete:
      tags: [ScheduleCandidatePlace]
      summary: Excluir
      description: Requer `forced=true` se o agendamento já estiver processado/finalizado.
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "204": { $ref: '#/components/responses/NoContent' } }

  /api/v1/s/rest/schedulegroupassessment/{key}/:
    get:
      tags: [GroupAssessmentSchedules]
      summary: Listar agendamentos de um grupo de agendamento
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }

  /api/v1/s/rest/scheduleallcandidates/{key}/:
    get:
      tags: [ScheduleAllCandidates]
      summary: Listar candidatos de um agendamento (presença/notas)
      parameters:
        - $ref: '#/components/parameters/KeyPath'
        - name: status_item_generated
          in: query
          schema: { type: string }
        - name: candidate_key
          in: query
          schema: { type: string }
        - name: initial_name
          in: query
          schema: { type: string }
      responses: { "200": { description: OK } }

  /api/v1/s/rest/schedulecandidate/{schedule_key}/{candidate_key}/:
    get:
      tags: [ScheduleCandidate]
      summary: Detalhe do candidato em um agendamento (questões, notas)
      parameters:
        - name: schedule_key
          in: path
          required: true
          schema: { type: string }
        - name: candidate_key
          in: path
          required: true
          schema: { type: string }
      responses: { "200": { description: OK } }

  /api/v1/s/rest/schedulenotnominal/{schedule_key}/:
    get:
      tags: [ScheduleNotNominal]
      summary: Listar provas não nominais de um agendamento
      parameters:
        - name: schedule_key
          in: path
          required: true
          schema: { type: string }
      responses: { "200": { description: OK } }
    post:
      tags: [ScheduleNotNominal]
      summary: Criar provas não nominais
      parameters:
        - name: schedule_key
          in: path
          required: true
          schema: { type: string }
      responses: { "200": { description: OK } }
    delete:
      tags: [ScheduleNotNominal]
      summary: Remover Schedule Not Nominal do agendamento
      description: Requer `forced=true` se já processado/finalizado.
      parameters:
        - name: schedule_key
          in: path
          required: true
          schema: { type: string }
      responses: { "204": { $ref: '#/components/responses/NoContent' } }

  /api/v1/s/rest/schedulenotnominal/{schedule_key}/{place}/:
    delete:
      tags: [ScheduleNotNominal]
      summary: Remover Schedule Not Nominal por agendamento e local
      description: Requer `forced=true` se já processado/finalizado.
      parameters:
        - name: schedule_key
          in: path
          required: true
          schema: { type: string }
        - name: place
          in: path
          required: true
          schema: { type: string }
      responses: { "204": { $ref: '#/components/responses/NoContent' } }

  /api/v1/s/rest/batch_schedule_create/:
    post:
      tags: [BatchSchedule]
      summary: Criar agendamentos em lote (máx. 1500)
      responses: { "201": { description: Criado } }

  /api/v1/s/rest/batch_schedulecandidatexplace/:
    post:
      tags: [BatchSchedule]
      summary: Criar vínculo Schedule x Candidate x Place em lote (máx. 5000)
      responses: { "201": { description: Criado } }

  /api/v1/s/rest/schedule/{schedule_key}/candidate/score_by_content/:
    get:
      tags: [ScoreXCandidateXContent]
      summary: Resultados dos candidatos de um agendamento agrupados por conteúdo
      parameters:
        - name: schedule_key
          in: path
          required: true
          schema: { type: string }
        - name: content_order
          in: query
          schema: { type: integer, default: 1 }
          description: Nível de conteúdo em que as notas serão agrupadas.
      responses: { "200": { description: OK } }

  /api/v1/wfl/rest/workflow/step/:
    get:
      tags: [WorkflowStep]
      summary: Listar passos por workflow
      responses: { "200": { description: OK } }

  /api/v1/wfl/rest/workflow/userxcontent/:
    get:
      tags: [WorkflowUserXContent]
      summary: Listar vínculos Workflow x Usuário x Conteúdo
      responses: { "200": { description: OK } }
    post:
      tags: [WorkflowUserXContent]
      summary: Criar vínculos Workflow x Usuário x Conteúdo
      responses: { "200": { description: OK } }

  /api/v1/wfl/rest/workflow/userxcontent/{workflow}/{legacy_key}/:
    get:
      tags: [WorkflowUserXContent]
      summary: Obter vínculo específico
      parameters:
        - name: workflow
          in: path
          required: true
          schema: { type: string }
        - name: legacy_key
          in: path
          required: true
          schema: { type: string }
      responses: { "200": { description: OK } }
    delete:
      tags: [WorkflowUserXContent]
      summary: Excluir vínculo
      parameters:
        - name: workflow
          in: path
          required: true
          schema: { type: string }
        - name: legacy_key
          in: path
          required: true
          schema: { type: string }
      responses: { "204": { $ref: '#/components/responses/NoContent' } }

  /api/v1/wfl/rest/workflow/userxcontent/{workflow}/:
    get:
      tags: [WorkflowUserXContent]
      summary: Listar vínculos filtrados por step, content e user
      parameters:
        - name: workflow
          in: path
          required: true
          schema: { type: string }
        - name: step
          in: query
          schema: { type: integer }
        - name: content
          in: query
          schema: { type: string }
        - name: user
          in: query
          schema: { type: integer }
      responses: { "200": { description: OK } }

  /api/v1/tm/rest/period/:
    get:
      tags: [Period]
      summary: Listar períodos letivos
      responses: { "200": { description: OK } }
    post:
      tags: [Period]
      summary: Criar período letivo
      responses: { "200": { description: OK } }

  /api/v1/tm/rest/period/{key}/:
    get:
      tags: [Period]
      summary: Obter período por key
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    put:
      tags: [Period]
      summary: Atualizar período (todos os campos)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    patch:
      tags: [Period]
      summary: Atualizar período (parcial)
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "200": { description: OK } }
    delete:
      tags: [Period]
      summary: Excluir período
      parameters: [ { $ref: '#/components/parameters/KeyPath' } ]
      responses: { "204": { $ref: '#/components/responses/NoContent' } }

  /api/v2/clientunit/add-user/:
    post:
      tags: [APIs V2]
      summary: Vincular usuário a um client unit
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                user_id: { type: string }
                client_unit_id: { type: string }
      responses: { "201": { description: Criado } }

  /api/v2/clientunit/remove-user/:
    post:
      tags: [APIs V2]
      summary: Desvincular usuário de um client unit
      responses: { "204": { $ref: '#/components/responses/NoContent' } }

  /api/v2/tm/candidate/:
    post:
      tags: [APIs V2]
      summary: Criar candidato (com username/password)
      responses: { "201": { description: Criado } }

  /api/v2/candidate/remove-candidate/:
    post:
      tags: [APIs V2]
      summary: Excluir candidato
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                candidate_id: { type: string }
      responses: { "204": { $ref: '#/components/responses/NoContent' } }

  /api/v2/content/add-owner/:
    post:
      tags: [APIs V2]
      summary: Adicionar usuário como dono de um conteúdo
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                user_id: { type: string }
                content_id: { type: string }
      responses: { "201": { description: Criado } }

  /api/v2/content/remove-owner/:
    post:
      tags: [APIs V2]
      summary: Remover usuário como dono de um conteúdo
      responses: { "204": { $ref: '#/components/responses/NoContent' } }
