openapi: 3.0.3
info:
  title: STCP DirectLink API
  version: v2
  description: |
    Especificação pública da API HTTP do STCP DirectLink.

    Substitua o host de exemplo pelo host fornecido pelo administrador do
    ambiente. Use HTTPS em produção. Links seguros retornados pela API devem
    ser usados integralmente, sem alteração dos parâmetros.

    Rotas protegidas aceitam um token Bearer no header Authorization ou o
    cookie de sessão __Host-dirl_session. Se ambos forem enviados, o header
    Authorization tem prioridade.
  contact:
    name: Administrador do serviço
servers:
  - url: https://api.exemplo.com/directlink/v2
    description: Host ilustrativo; substitua pelo endereço do seu ambiente.
tags:
  - name: Disponibilidade
  - name: Autenticação
  - name: Sessão
  - name: Transferências
  - name: Relatórios
  - name: Single Sign-On
paths:
  /healthcheck:
    get:
      tags: [Disponibilidade]
      summary: Verificar disponibilidade
      operationId: getHealthcheck
      security: []
      responses:
        '200':
          description: Serviço acessível.
          headers:
            Cache-Control:
              description: Impede armazenamento da resposta em cache.
              schema:
                type: string
                enum: [no-store]
          content:
            application/json:
              schema:
                type: object
                required: [status, service, timestamp, uptime_seconds]
                properties:
                  status:
                    type: string
                    enum: [ok]
                  service:
                    type: string
                    enum: [STCPDirectLink]
                  timestamp:
                    type: string
                    format: date-time
                    description: Horário UTC da verificação em RFC 3339.
                  uptime_seconds:
                    type: integer
                    format: int64
                    minimum: 0
                    description: Segundos desde a inicialização do serviço HTTP.
              example:
                status: ok
                service: STCPDirectLink
                timestamp: '2026-10-16T19:29:23Z'
                uptime_seconds: 86400
  /auth/login:
    get:
      tags: [Autenticação]
      summary: Iniciar sessão local
      description: |
        Autentica com HTTP Basic. Se MFA for necessário, a resposta `206`
        inclui um link seguro para concluir o desafio. Siga o link integral
        retornado; não reconstrua seus parâmetros.
        Quando a validação Cloudflare Turnstile está habilitada, envie o token do
        widget no header `CF-Turnstile-Response`; ele é validado antes das
        credenciais.
      operationId: loginGet
      security:
        - basicAuth: []
      parameters:
        - $ref: '#/components/parameters/TurnstileResponse'
      responses:
        '200':
          description: Login concluído; a sessão é estabelecida por cookie.
          headers:
            Set-Cookie:
              $ref: '#/components/headers/SessionCookie'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginResponse'
        '206':
          description: É necessária uma etapa adicional de MFA.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MfaChallengeResponse'
        '400':
          description: Credencial ausente ou header `CF-Turnstile-Response` ausente ou inválido.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Acesso negado ou token Turnstile rejeitado (inválido, expirado ou já utilizado).
        '503':
          description: Não foi possível validar o token Turnstile com a Cloudflare.
    post:
      tags: [Autenticação]
      summary: Iniciar sessão local
      description: |
        Autentica com HTTP Basic. Se MFA for necessário, a resposta `206`
        inclui um link seguro para concluir o desafio. Siga o link integral
        retornado; não reconstrua seus parâmetros.
        Quando a validação Cloudflare Turnstile está habilitada, envie o token do
        widget no header `CF-Turnstile-Response`; ele é validado antes das
        credenciais.
      operationId: loginPost
      security:
        - basicAuth: []
      parameters:
        - $ref: '#/components/parameters/TurnstileResponse'
      responses:
        '200':
          description: Login concluído; a sessão é estabelecida por cookie.
          headers:
            Set-Cookie:
              $ref: '#/components/headers/SessionCookie'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginResponse'
        '206':
          description: É necessária uma etapa adicional de MFA.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MfaChallengeResponse'
        '400':
          description: Credencial ausente ou header `CF-Turnstile-Response` ausente ou inválido.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Acesso negado ou token Turnstile rejeitado (inválido, expirado ou já utilizado).
        '503':
          description: Não foi possível validar o token Turnstile com a Cloudflare.
  /auth/mfa:
    get:
      tags:
        - Autenticação
      summary: 'Validar desafio MFA (código na query string)'
      description: 'Valida o segundo fator pedido pelo login (`status` `1` ou `2` na resposta `206`). Use a URL integral recebida em `linkURL`. Prefira `POST`, com o código no formulário.'
      operationId: getMfaChallenge
      security: []
      parameters:
        - name: p
          in: query
          required: true
          schema:
            type: string
          description: 'PIN enviado por e-mail ou SMS, ou código TOTP.'
      responses:
        '200':
          description: 'Segundo fator validado; a sessão é estabelecida por cookie.'
          headers:
            Set-Cookie:
              $ref: '#/components/headers/SessionCookie'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginResponse'
        '400':
          description: 'Link inválido ou expirado, ou campo `p` ausente.'
        '403':
          description: 'Código TOTP inválido, usuário bloqueado ou não encontrado.'
        '405':
          description: 'Método diferente de `GET` e `POST`.'
    post:
      tags:
        - Autenticação
      summary: 'Validar desafio MFA'
      description: 'Valida o segundo fator pedido pelo login (`status` `1` ou `2` na resposta `206`). Use a URL integral recebida em `linkURL`. Prefira `POST`, com o código no formulário.'
      operationId: postMfaChallenge
      security: []
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required: [p]
              properties:
                p:
                  type: string
                  description: 'PIN enviado por e-mail ou SMS, ou código TOTP.'
      responses:
        '200':
          description: 'Segundo fator validado; a sessão é estabelecida por cookie.'
          headers:
            Set-Cookie:
              $ref: '#/components/headers/SessionCookie'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginResponse'
        '400':
          description: 'Link inválido ou expirado, ou campo `p` ausente.'
        '403':
          description: 'Código TOTP inválido, usuário bloqueado ou não encontrado.'
        '405':
          description: 'Método diferente de `GET` e `POST`.'
  /auth/mfa/register:
    get:
      tags:
        - Autenticação
      summary: 'Confirmar cadastro TOTP (código na query string)'
      description: 'Confirma o cadastro do TOTP pedido pelo login (`status` `3` na resposta `206`). Envie o código gerado pelo aplicativo autenticador com o segredo recebido. Não estabelece sessão: depois do cadastro, faça o login de novo.'
      operationId: getMfaRegistration
      security: []
      parameters:
        - name: p
          in: query
          required: true
          schema:
            type: string
          description: 'Código TOTP gerado pelo aplicativo autenticador.'
      responses:
        '200':
          description: 'TOTP cadastrado. Não estabelece sessão.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MfaRegisterResponse'
        '400':
          description: 'Link inválido ou expirado, ou campo `p` ausente.'
        '403':
          description: 'Código TOTP inválido ou falha ao gravar o segredo.'
        '405':
          description: 'Método diferente de `GET` e `POST`.'
    post:
      tags:
        - Autenticação
      summary: 'Confirmar cadastro TOTP'
      description: 'Confirma o cadastro do TOTP pedido pelo login (`status` `3` na resposta `206`). Envie o código gerado pelo aplicativo autenticador com o segredo recebido. Não estabelece sessão: depois do cadastro, faça o login de novo.'
      operationId: postMfaRegistration
      security: []
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required: [p]
              properties:
                p:
                  type: string
                  description: 'Código TOTP gerado pelo aplicativo autenticador.'
      responses:
        '200':
          description: 'TOTP cadastrado. Não estabelece sessão.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MfaRegisterResponse'
        '400':
          description: 'Link inválido ou expirado, ou campo `p` ausente.'
        '403':
          description: 'Código TOTP inválido ou falha ao gravar o segredo.'
        '405':
          description: 'Método diferente de `GET` e `POST`.'
  /auth/token:
    post:
      tags: [Autenticação]
      summary: Emitir token ou trocar token de origem
      description: |
        Aceita password grant e token exchange. O password grant pode estar
        desabilitado no ambiente e não faz parte do OAuth 2.1. Para token exchange, o campo
        `provider` do formulário prevalece sobre
        `X-STCPDIRECTLINK-JWT-ISSUER`. Esse header não autentica chamadas
        protegidas.
      operationId: postAuthToken
      security: []
      parameters:
        - $ref: '#/components/parameters/JwtIssuer'
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              oneOf:
                - $ref: '#/components/schemas/PasswordGrantRequest'
                - $ref: '#/components/schemas/TokenExchangeRequest'
      responses:
        '200':
          description: Token de acesso emitido.
          headers:
            Cache-Control:
              schema:
                type: string
              example: no-store
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponse'
        '400':
          $ref: '#/components/responses/OAuthError'
  /oauth2/token:
    post:
      tags: [Autenticação]
      summary: Emitir token ou trocar token de origem
      description: Alias de `POST /auth/token`.
      operationId: postOauth2Token
      security: []
      parameters:
        - $ref: '#/components/parameters/JwtIssuer'
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              oneOf:
                - $ref: '#/components/schemas/PasswordGrantRequest'
                - $ref: '#/components/schemas/TokenExchangeRequest'
      responses:
        '200':
          description: Token de acesso emitido.
          headers:
            Cache-Control:
              schema:
                type: string
              example: no-store
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponse'
        '400':
          $ref: '#/components/responses/OAuthError'
  /auth/firstaccess:
    get:
      tags: [Autenticação]
      summary: Iniciar primeiro acesso
      description: |
        Quando a validação Cloudflare Turnstile está habilitada, envie o token do
        widget no header `CF-Turnstile-Response`; ele é validado antes do
        processamento.
      operationId: getFirstAccess
      security: []
      parameters:
        - $ref: '#/components/parameters/Username'
        - $ref: '#/components/parameters/TurnstileResponse'
      responses:
        '200':
          description: Link seguro em `linkURL`; PIN enviado ao usuário por e-mail ou SMS.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SecureLinkResponse'
        '400':
          description: Parâmetro ausente ou header `CF-Turnstile-Response` ausente ou inválido.
        '403':
          description: Acesso negado ou token Turnstile rejeitado (inválido, expirado ou já utilizado).
        '503':
          description: Não foi possível validar o token Turnstile com a Cloudflare.
  /auth/forget:
    get:
      tags: [Autenticação]
      summary: Iniciar recuperação de senha
      description: |
        Quando a validação Cloudflare Turnstile está habilitada, envie o token do
        widget no header `CF-Turnstile-Response`; ele é validado antes do
        processamento.
      operationId: getPasswordRecovery
      security: []
      parameters:
        - $ref: '#/components/parameters/Username'
        - $ref: '#/components/parameters/TurnstileResponse'
      responses:
        '200':
          description: Link seguro em `linkURL`; PIN enviado ao usuário por e-mail ou SMS.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SecureLinkResponse'
        '400':
          description: Parâmetro ausente ou header `CF-Turnstile-Response` ausente ou inválido.
        '403':
          description: Acesso negado ou token Turnstile rejeitado (inválido, expirado ou já utilizado).
        '503':
          description: Não foi possível validar o token Turnstile com a Cloudflare.
  /auth/change:
    post:
      tags: [Autenticação]
      summary: Concluir alteração de senha
      description: |
        Use o `linkURL` integral devolvido por `/auth/firstaccess` ou
        `/auth/forget`, com o `k` na URL. O link carrega os parâmetros protegidos
        de identificação e validade; não os construa manualmente.
        Aceita somente `POST`. Envie `p` e `z` no corpo do formulário, nunca na URL.
      operationId: postPasswordChange
      security: []
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required: [p, z]
              properties:
                p:
                  type: string
                  description: PIN ou código exigido pelo fluxo.
                z:
                  type: string
                  format: password
                  description: Nova senha.
      responses:
        '200':
          description: Senha alterada.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OperationResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          description: A troca de senha foi recusada.
        '405':
          description: Método diferente de `POST`.
  /auth/logout:
    get:
      tags: [Sessão]
      summary: Encerrar sessão
      operationId: getLogout
      security:
        - bearerAuth: []
        - sessionCookie: []
      responses:
        '200':
          description: Sessão encerrada.
          headers:
            Set-Cookie:
              $ref: '#/components/headers/ClearedSessionCookie'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      tags: [Sessão]
      summary: Encerrar sessão
      operationId: postLogout
      security:
        - bearerAuth: []
        - sessionCookie: []
      responses:
        '200':
          description: Sessão encerrada.
          headers:
            Set-Cookie:
              $ref: '#/components/headers/ClearedSessionCookie'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /upload:
    post:
      tags: [Transferências]
      summary: Enviar arquivo
      operationId: postUpload
      security:
        - bearerAuth: []
        - sessionCookie: []
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              $ref: '#/components/schemas/MultipartUpload'
            encoding:
              file:
                contentType: application/octet-stream
          application/octet-stream:
            schema:
              type: string
              format: binary
      parameters:
        - in: header
          name: X-STCPDIRECTLINK-FILENAME
          required: false
          description: Obrigatório quando o corpo é application/octet-stream.
          schema:
            type: string
        - in: header
          name: X-STCPDIRECTLINK-DESTINATION
          required: false
          schema:
            type: string
        - in: header
          name: X-STCPDIRECTLINK-EMAIL
          required: false
          schema:
            type: string
        - in: header
          name: X-STCPDIRECTLINK-MOBILE
          required: false
          schema:
            type: string
        - in: header
          name: X-STCPDIRECTLINK-HASH
          required: false
          schema:
            type: string
        - in: header
          name: X-STCPDIRECTLINK-EMAIL-FROM
          required: false
          schema:
            type: string
        - in: header
          name: X-STCPDIRECTLINK-INFO
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Arquivo recebido.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UploadResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          description: "Conexão bloqueada para o usuário: usuário bloqueado, fora do intervalo de horário permitido ou recusada pelo programa externo de validação."
  /download/list:
    get:
      tags: [Transferências]
      summary: Listar arquivos disponíveis
      operationId: getDownloadList
      security:
        - bearerAuth: []
        - sessionCookie: []
      parameters:
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Página de arquivos disponíveis.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FileListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /download/list/backup:
    get:
      tags: [Transferências]
      summary: Listar arquivos de backup
      operationId: getBackupDownloadList
      security:
        - bearerAuth: []
        - sessionCookie: []
      parameters:
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Página de arquivos de backup.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FileListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /download/file:
    get:
      tags: [Transferências]
      summary: Baixar arquivo listado
      description: |
        Use a URL `linkURL` integral da listagem, com o token Bearer ou o cookie de sessão
        do mesmo usuário que fez a listagem. O link sozinho não dá acesso ao arquivo.
      operationId: getDownloadFile
      security:
        - bearerAuth: []
        - sessionCookie: []
      responses:
        '200':
          description: Conteúdo do arquivo.
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '400':
          description: Credencial ausente.
        '401':
          description: Token inválido ou expirado.
        '403':
          description: O token é de outro usuário, o download está desabilitado ou o acesso foi bloqueado.
        '404':
          description: Arquivo ou link não encontrado.
  /download/file/backup:
    get:
      tags: [Transferências]
      summary: Baixar arquivo de backup
      description: |
        Use a URL `linkURL` integral da listagem de backup, com o token Bearer ou o
        cookie de sessão do mesmo usuário que fez a listagem.
      operationId: getBackupDownloadFile
      security:
        - bearerAuth: []
        - sessionCookie: []
      responses:
        '200':
          description: Conteúdo do arquivo de backup.
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '400':
          description: Credencial ausente.
        '401':
          description: Token inválido ou expirado.
        '403':
          description: O token é de outro usuário, o download está desabilitado ou o acesso foi bloqueado.
        '404':
          description: Arquivo ou link não encontrado.
  /download:
    get:
      tags: [Transferências]
      summary: Baixar arquivo compartilhado
      description: |
        Abra a URL segura integral enviada ao destinatário. Quando o link
        exigir PIN, envie-o como parâmetro `p` sem modificar os demais
        parâmetros do link.
      operationId: getSharedDownload
      security: []
      parameters:
        - in: query
          name: p
          required: false
          description: PIN exigido pelo link seguro, quando aplicável.
          schema:
            type: string
      responses:
        '200':
          description: Conteúdo do arquivo.
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '404':
          description: Arquivo ou link não encontrado.
  /download/pin:
    get:
      tags: [Transferências]
      summary: Enviar PIN e obter link de download
      description: Use a URL integral e segura emitida para iniciar o envio do PIN.
      operationId: getDownloadPin
      security: []
      responses:
        '200':
          description: PIN enviado; resposta contém o link de download.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DownloadPinResponse'
  /report:
    get:
      tags: [Relatórios]
      summary: Consultar relatório de transferências
      operationId: getReport
      security:
        - bearerAuth: []
        - sessionCookie: []
      parameters:
        - $ref: '#/components/parameters/Page'
        - in: query
          name: days
          required: false
          description: Janela consultada em dias; padrão 1.
          schema:
            type: integer
            default: 1
        - in: query
          name: filterdirection
          required: true
          description: Tipo de transferência; 5 = downloads concluídos (evento STCPEVTENDSENDFILE), 7 = uploads concluídos (evento STCPEVTENDRECEIVEFILE). Outros valores retornam 400.
          schema:
            type: integer
            enum: [5, 7]
        - in: query
          name: filterfile
          required: false
          description: Filtra pelo nome do arquivo.
          schema:
            type: string
      responses:
        '200':
          description: Registros de transferência.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReportResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /sso/login:
    get:
      tags: [Single Sign-On]
      summary: Iniciar login federado
      description: Inicia o fluxo de navegador e redireciona ao provedor.
      operationId: getSsoLogin
      security: []
      parameters:
        - $ref: '#/components/parameters/Provider'
      responses:
        '302':
          description: Redirecionamento para o provedor de identidade.
          headers:
            Location:
              schema:
                type: string
                format: uri
  /sso/logout:
    get:
      tags: [Single Sign-On]
      summary: Iniciar logout federado
      operationId: getSsoLogout
      security:
        - bearerAuth: []
        - sessionCookie: []
      parameters:
        - $ref: '#/components/parameters/Provider'
      responses:
        '302':
          description: Redirecionamento para o provedor ou destino de logout.
          headers:
            Location:
              schema:
                type: string
                format: uri
        '401':
          $ref: '#/components/responses/Unauthorized'
  /sso/oauth2/cb:
    get:
      tags: [Single Sign-On]
      summary: Receber callback OAuth 2.0 e 2.1/OpenID Connect
      description: Chamado pelo provedor como parte do fluxo de login.
      operationId: getOauthCallback
      security: []
      parameters:
        - in: query
          name: state
          required: true
          schema:
            type: string
        - in: query
          name: code
          required: true
          schema:
            type: string
      responses:
        '302':
          description: Redirecionamento após processar o callback.
          headers:
            Location:
              schema:
                type: string
                format: uri
    post:
      tags: [Single Sign-On]
      summary: Receber callback OAuth 2.0 e 2.1/OpenID Connect
      operationId: postOauthCallback
      security: []
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required: [state, code]
              properties:
                state:
                  type: string
                code:
                  type: string
      responses:
        '302':
          description: Redirecionamento após processar o callback.
          headers:
            Location:
              schema:
                type: string
                format: uri
  /sso/oauth2/logout/cb:
    get:
      tags: [Single Sign-On]
      summary: Receber callback de logout OAuth 2.0
      operationId: getOauthLogoutCallback
      security: []
      parameters:
        - in: query
          name: state
          required: true
          schema:
            type: string
        - $ref: '#/components/parameters/Provider'
      responses:
        '302':
          description: Redirecionamento após processar o callback.
          headers:
            Location:
              schema:
                type: string
                format: uri
    post:
      tags: [Single Sign-On]
      summary: Receber callback de logout OAuth 2.0
      operationId: postOauthLogoutCallback
      security: []
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required: [state, provider]
              properties:
                state:
                  type: string
                provider:
                  type: string
      responses:
        '302':
          description: Redirecionamento após processar o callback.
          headers:
            Location:
              schema:
                type: string
                format: uri
  /sso/saml2/acs:
    post:
      tags: [Single Sign-On]
      summary: Receber asserção SAML
      description: Endpoint ACS chamado pelo provedor SAML via HTTP POST.
      operationId: postSamlAcs
      security: []
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required: [SAMLResponse]
              properties:
                SAMLResponse:
                  type: string
                  description: Asserção SAML codificada em Base64.
                RelayState:
                  type: string
      responses:
        '302':
          description: Redirecionamento após processar a asserção.
          headers:
            Location:
              schema:
                type: string
                format: uri
  /sso/saml2/slo:
    get:
      tags: [Single Sign-On]
      summary: Receber mensagem de logout SAML
      operationId: getSamlSlo
      security: []
      parameters:
        - in: query
          name: SAMLResponse
          required: false
          schema:
            type: string
        - in: query
          name: RelayState
          required: false
          schema:
            type: string
      responses:
        '302':
          description: Redirecionamento após processar logout.
          headers:
            Location:
              schema:
                type: string
                format: uri
    post:
      tags: [Single Sign-On]
      summary: Receber mensagem de logout SAML
      operationId: postSamlSlo
      security: []
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                SAMLResponse:
                  type: string
                RelayState:
                  type: string
      responses:
        '302':
          description: Redirecionamento após processar logout.
          headers:
            Location:
              schema:
                type: string
                format: uri
  /sso/saml2/metadata:
    get:
      tags: [Single Sign-On]
      summary: Obter metadados SAML por query
      operationId: getSamlMetadataByQuery
      security: []
      parameters:
        - $ref: '#/components/parameters/Provider'
      responses:
        '200':
          description: Metadados XML do provedor.
          content:
            application/xml:
              schema:
                type: string
  /sso/saml2/metadata/{provider}:
    get:
      tags: [Single Sign-On]
      summary: Obter metadados SAML por caminho
      operationId: getSamlMetadataByPath
      security: []
      parameters:
        - name: provider
          in: path
          required: true
          description: Identificador cadastrado do provedor SAML.
          schema:
            type: string
      responses:
        '200':
          description: Metadados XML do provedor.
          content:
            application/xml:
              schema:
                type: string
components:
  securitySchemes:
    basicAuth:
      type: http
      scheme: basic
    bearerAuth:
      type: http
      scheme: bearer
      description: Token de acesso emitido por um endpoint OAuth.
    sessionCookie:
      type: apiKey
      in: cookie
      name: __Host-dirl_session
      description: Cookie de sessão emitido após login local ou SSO.
  headers:
    SessionCookie:
      description: Cookie seguro de sessão retornado após autenticação.
      schema:
        type: string
      example: __Host-dirl_session=<token>; Path=/; Secure; HttpOnly; SameSite=Strict
    ClearedSessionCookie:
      description: Cookie de sessão limpo quando aplicável.
      schema:
        type: string
  parameters:
    TurnstileResponse:
      in: header
      name: CF-Turnstile-Response
      required: false
      description: Token gerado pelo widget Cloudflare Turnstile. Obrigatório quando `turnstileEnable` está habilitado na configuração; ignorado caso contrário. O token é de uso único.
      schema:
        type: string
        maxLength: 2048
    JwtIssuer:
      in: header
      name: X-STCPDIRECTLINK-JWT-ISSUER
      required: false
      description: Provider usado para validar o JWT de origem no token exchange.
      schema:
        type: string
    Username:
      in: query
      name: u
      required: true
      description: Identificação da conta.
      schema:
        type: string
    Page:
      in: query
      name: page
      required: false
      description: Índice da página; padrão 0.
      schema:
        type: integer
        default: 0
    Provider:
      in: query
      name: provider
      required: true
      description: Identificador do provedor configurado.
      schema:
        type: string
  requestBodies:
    TokenRequest:
      required: true
      content:
        application/x-www-form-urlencoded:
          schema:
            oneOf:
              - $ref: '#/components/schemas/PasswordGrantRequest'
              - $ref: '#/components/schemas/TokenExchangeRequest'
  responses:
    BadRequest:
      description: Requisição ou parâmetro inválido.
    Unauthorized:
      description: Credencial ou token não aceito.
    OAuthError:
      description: Falha na validação do grant ou token de origem.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/OAuthError'
  schemas:
    OperationResponse:
      type: object
      properties:
        status:
          type: integer
          example: 0
        description:
          type: string
        apiver:
          type: string
          example: v2 (9.0.1)
        user:
          type: string
        length:
          type: integer
          format: int64
        time:
          type: string
          format: date-time
    LoginResponse:
      type: object
      properties:
        status:
          type: integer
          example: 0
        description:
          type: string
        apiver:
          type: string
          example: v2 (9.0.1)
        user:
          type: string
        urllink:
          type: string
          format: uri
        time:
          type: string
          format: date-time
    MfaChallengeResponse:
      type: object
      properties:
        status:
          type: integer
          enum: [1, 2, 3]
          description: 'Etapa do MFA: `1` PIN por e-mail ou SMS (`/auth/mfa`), `2` TOTP cadastrado (`/auth/mfa`), `3` cadastro do TOTP (`/auth/mfa/register`).'
        description:
          type: string
        apiver:
          type: string
          example: v2 (9.0.1)
        user:
          type: string
        linkURL:
          type: string
          format: uri
          description: 'URL segura integral para concluir a etapa.'
        secret:
          type: string
          format: password
          description: 'Somente no `status` `3`: segredo TOTP em Base32. Vazio no `status` `2`; ausente no `status` `1`. Dado sensível.'
        qrcode:
          type: string
          description: 'Somente no `status` `3`: QR code do segredo, imagem PNG de 200 × 200 em Base64 URL-safe (RFC 4648, com `-` e `_`). Converta para Base64 padrão para usar em `data:image/png;base64,`. Vazio no `status` `2`; ausente no `status` `1`.'
        time:
          type: string
          format: date-time
    MfaRegisterResponse:
      type: object
      properties:
        status:
          type: integer
          example: 0
        description:
          type: string
          example: handlerAuthMfaRegister:succsessfully
        apiver:
          type: string
          example: v2 (9.0.1)
        user:
          type: string
        length:
          type: integer
          description: 'Sempre `0`.'
        time:
          type: string
    SecureLinkResponse:
      type: object
      properties:
        status:
          type: integer
          example: 0
        description:
          type: string
        apiver:
          type: string
          example: v2 (9.0.1)
        user:
          type: string
        linkURL:
          type: string
          format: uri
        time:
          type: string
          format: date-time
    PasswordGrantRequest:
      type: object
      required: [grant_type, username, password]
      properties:
        grant_type:
          type: string
          enum: [password]
        username:
          type: string
        password:
          type: string
          format: password
    TokenExchangeRequest:
      type: object
      required: [grant_type, subject_token_type, subject_token]
      properties:
        grant_type:
          type: string
          enum:
            - urn:ietf:params:oauth:grant-type:token-exchange
        subject_token_type:
          type: string
          enum:
            - urn:ietf:params:oauth:token-type:jwt
        subject_token:
          type: string
          description: JWT de origem.
        provider:
          type: string
          description: Provider que valida o token; prevalece sobre o header equivalente.
    TokenResponse:
      type: object
      required: [access_token, issued_token_type, token_type, expires_in, scope]
      properties:
        access_token:
          type: string
        issued_token_type:
          type: string
          enum:
            - urn:ietf:params:oauth:token-type:access_token
        token_type:
          type: string
          example: Bearer
        expires_in:
          type: integer
          format: int64
          description: Validade do token em segundos; varia conforme a instância.
        scope:
          type: string
          description: URI da API configurada; não representa lista de permissões.
    OAuthError:
      type: object
      properties:
        error:
          type: integer
          description: Código numérico da condição reportada.
        error_description:
          type: string
        error_uri:
          type: string
          format: uri
    MultipartUpload:
      type: object
      required: [file]
      properties:
        file:
          type: string
          format: binary
        d:
          type: string
          description: Destinatário ou usuário de destino.
        m:
          type: string
          description: E-mail associado à transferência.
        c:
          type: string
          description: Celular associado à transferência.
        h:
          type: string
          description: Hash SHA-256 esperado do arquivo.
        n:
          type: string
          description: Identificação ou e-mail do remetente.
        i:
          type: string
          description: Informação adicional da transferência.
    UploadResponse:
      allOf:
        - $ref: '#/components/schemas/OperationResponse'
        - type: object
          properties:
            id:
              type: string
            destination:
              type: string
            length:
              type: integer
              format: int64
            linkURL:
              type: array
              items:
                type: string
                format: uri
    FileListResponse:
      allOf:
        - $ref: '#/components/schemas/OperationResponse'
        - type: object
          properties:
            files:
              type: array
              items:
                $ref: '#/components/schemas/FileEntry'
            page_next:
              type: string
    FileEntry:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        length:
          type: integer
          format: int64
        modified:
          type: integer
          format: int64
          description: Unix time em milissegundos.
        linkURL:
          type: string
          format: uri
          description: Link assinado; use a URL integral.
    DownloadPinResponse:
      allOf:
        - $ref: '#/components/schemas/OperationResponse'
        - type: object
          properties:
            email:
              type: string
            phone:
              type: string
            file:
              type: string
            linkURL:
              type: string
              format: uri
    ReportResponse:
      allOf:
        - $ref: '#/components/schemas/OperationResponse'
        - type: object
          properties:
            page:
              type: integer
            logs:
              type: array
              items:
                type: object
                additionalProperties: true
