openapi: 3.0.3
info:
  title: STCP DirectLink API
  version: v2
  description: |
    Especificación pública de la API HTTP de STCP DirectLink.

    Sustituya el host de ejemplo por el proporcionado por el administrador del entorno. Utilice HTTPS en producción. Utilice los enlaces seguros devueltos por la API completos, sin modificar sus parámetros.

    Las rutas protegidas aceptan un token Bearer en la cabecera Authorization o la cookie de sesión __Host-dirl_session. Si se envían ambos, la cabecera Authorization tiene prioridad.
  contact:
    name: Administrador del servicio
servers:
  - url: https://api.exemplo.com/directlink/v2
    description: Host de ejemplo; sustitúyalo por la dirección de su entorno.
tags:
  - name: Disponibilidad
  - name: Autenticación
  - name: Sesión
  - name: Transferencias
  - name: Informes
  - name: Single Sign-On
paths:
  /healthcheck:
    get:
      tags:
        - Disponibilidad
      summary: Comprobar disponibilidad
      operationId: getHealthcheck
      security: []
      responses:
        '200':
          description: Servicio accesible.
          headers:
            Cache-Control:
              description: Impide el almacenamiento de la respuesta en caché.
              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: Hora UTC de la comprobación en formato RFC 3339.
                  uptime_seconds:
                    type: integer
                    format: int64
                    minimum: 0
                    description: Segundos desde el inicio del servicio HTTP.
              example:
                status: ok
                service: STCPDirectLink
                timestamp: '2026-10-16T19:29:23Z'
                uptime_seconds: 86400
  /auth/login:
    get:
      tags:
        - Autenticación
      summary: Iniciar sesión local
      description: |
        Autentica mediante HTTP Basic. Si se requiere MFA, la respuesta `206` incluye un enlace seguro para completar el desafío. Utilice el enlace completo devuelto; no reconstruya sus parámetros.
        Cuando la validación Cloudflare Turnstile está habilitada, envíe el token
        del widget en el header `CF-Turnstile-Response`; se valida antes de las
        credenciales.
      operationId: loginGet
      security:
        - basicAuth: []
      parameters:
        - $ref: '#/components/parameters/TurnstileResponse'
      responses:
        '200':
          description: Inicio de sesión completado; la sesión se establece mediante una cookie.
          headers:
            Set-Cookie:
              $ref: '#/components/headers/SessionCookie'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginResponse'
        '206':
          description: Se requiere un paso adicional de MFA.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MfaChallengeResponse'
        '400':
          description: Credencial ausente o header `CF-Turnstile-Response` ausente o no válido.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Acceso denegado o token Turnstile rechazado (no válido, caducado o ya utilizado).
        '503':
          description: No fue posible validar el token Turnstile con Cloudflare.
    post:
      tags:
        - Autenticación
      summary: Iniciar sesión local
      description: |
        Autentica mediante HTTP Basic. Si se requiere MFA, la respuesta `206` incluye un enlace seguro para completar el desafío. Utilice el enlace completo devuelto; no reconstruya sus parámetros.
        Cuando la validación Cloudflare Turnstile está habilitada, envíe el token
        del widget en el header `CF-Turnstile-Response`; se valida antes de las
        credenciales.
      operationId: loginPost
      security:
        - basicAuth: []
      parameters:
        - $ref: '#/components/parameters/TurnstileResponse'
      responses:
        '200':
          description: Inicio de sesión completado; la sesión se establece mediante una cookie.
          headers:
            Set-Cookie:
              $ref: '#/components/headers/SessionCookie'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginResponse'
        '206':
          description: Se requiere un paso adicional de MFA.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MfaChallengeResponse'
        '400':
          description: Credencial ausente o header `CF-Turnstile-Response` ausente o no válido.
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          description: Acceso denegado o token Turnstile rechazado (no válido, caducado o ya utilizado).
        '503':
          description: No fue posible validar el token Turnstile con Cloudflare.
  /auth/mfa:
    get:
      tags:
        - Autenticación
      summary: 'Validar desafío MFA (código en la query string)'
      description: 'Valida el segundo factor pedido por el inicio de sesión (`status` `1` o `2` en la respuesta `206`). Use la URL completa recibida en `linkURL`. Prefiera `POST`, con el código en el formulario.'
      operationId: getMfaChallenge
      security: []
      parameters:
        - name: p
          in: query
          required: true
          schema:
            type: string
          description: 'PIN enviado por correo electrónico o SMS, o código TOTP.'
      responses:
        '200':
          description: 'Segundo factor validado; la sesión se establece por cookie.'
          headers:
            Set-Cookie:
              $ref: '#/components/headers/SessionCookie'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginResponse'
        '400':
          description: 'Enlace no válido o expirado, o campo `p` ausente.'
        '403':
          description: 'Código TOTP no válido, usuario bloqueado o no encontrado.'
        '405':
          description: 'Método diferente de `GET` y `POST`.'
    post:
      tags:
        - Autenticación
      summary: 'Validar desafío MFA'
      description: 'Valida el segundo factor pedido por el inicio de sesión (`status` `1` o `2` en la respuesta `206`). Use la URL completa recibida en `linkURL`. Prefiera `POST`, con el código en el formulario.'
      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 correo electrónico o SMS, o código TOTP.'
      responses:
        '200':
          description: 'Segundo factor validado; la sesión se establece por cookie.'
          headers:
            Set-Cookie:
              $ref: '#/components/headers/SessionCookie'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LoginResponse'
        '400':
          description: 'Enlace no válido o expirado, o campo `p` ausente.'
        '403':
          description: 'Código TOTP no válido, usuario bloqueado o no encontrado.'
        '405':
          description: 'Método diferente de `GET` y `POST`.'
  /auth/mfa/register:
    get:
      tags:
        - Autenticación
      summary: 'Confirmar registro TOTP (código en la query string)'
      description: 'Confirma el registro del TOTP pedido por el inicio de sesión (`status` `3` en la respuesta `206`). Envíe el código generado por la aplicación autenticadora con el secreto recibido. No establece sesión: después del registro, inicie sesión de nuevo.'
      operationId: getMfaRegistration
      security: []
      parameters:
        - name: p
          in: query
          required: true
          schema:
            type: string
          description: 'Código TOTP generado por la aplicación autenticadora.'
      responses:
        '200':
          description: 'TOTP registrado. No establece sesión.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MfaRegisterResponse'
        '400':
          description: 'Enlace no válido o expirado, o campo `p` ausente.'
        '403':
          description: 'Código TOTP no válido o falla al guardar el secreto.'
        '405':
          description: 'Método diferente de `GET` y `POST`.'
    post:
      tags:
        - Autenticación
      summary: 'Confirmar registro TOTP'
      description: 'Confirma el registro del TOTP pedido por el inicio de sesión (`status` `3` en la respuesta `206`). Envíe el código generado por la aplicación autenticadora con el secreto recibido. No establece sesión: después del registro, inicie sesión de nuevo.'
      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 generado por la aplicación autenticadora.'
      responses:
        '200':
          description: 'TOTP registrado. No establece sesión.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MfaRegisterResponse'
        '400':
          description: 'Enlace no válido o expirado, o campo `p` ausente.'
        '403':
          description: 'Código TOTP no válido o falla al guardar el secreto.'
        '405':
          description: 'Método diferente de `GET` y `POST`.'
  /auth/token:
    post:
      tags:
        - Autenticación
      summary: Emitir un token o intercambiar un token de origen
      description: |
        Acepta password grant y token exchange. El password grant puede estar deshabilitado en el entorno y no forma parte de OAuth 2.1. Para token exchange, el campo `provider` del formulario tiene prioridad sobre `X-STCPDIRECTLINK-JWT-ISSUER`. Esta cabecera no autentica solicitudes 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 acceso 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:
        - Autenticación
      summary: Emitir un token o intercambiar un token de origen
      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 acceso 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:
        - Autenticación
      summary: Iniciar primer acceso
      description: |
        Cuando la validación Cloudflare Turnstile está habilitada, envíe el token
        del widget en el header `CF-Turnstile-Response`; se valida antes del
        procesamiento.
      operationId: getFirstAccess
      security: []
      parameters:
        - $ref: '#/components/parameters/Username'
        - $ref: '#/components/parameters/TurnstileResponse'
      responses:
        '200':
          description: Enlace seguro en `linkURL`; PIN enviado al usuario por correo electrónico o SMS.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SecureLinkResponse'
        '400':
          description: Parámetro ausente o header `CF-Turnstile-Response` ausente o no válido.
        '403':
          description: Acceso denegado o token Turnstile rechazado (no válido, caducado o ya utilizado).
        '503':
          description: No fue posible validar el token Turnstile con Cloudflare.
  /auth/forget:
    get:
      tags:
        - Autenticación
      summary: Iniciar recuperación de contraseña
      description: |
        Cuando la validación Cloudflare Turnstile está habilitada, envíe el token
        del widget en el header `CF-Turnstile-Response`; se valida antes del
        procesamiento.
      operationId: getPasswordRecovery
      security: []
      parameters:
        - $ref: '#/components/parameters/Username'
        - $ref: '#/components/parameters/TurnstileResponse'
      responses:
        '200':
          description: Enlace seguro en `linkURL`; PIN enviado al usuario por correo electrónico o SMS.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SecureLinkResponse'
        '400':
          description: Parámetro ausente o header `CF-Turnstile-Response` ausente o no válido.
        '403':
          description: Acceso denegado o token Turnstile rechazado (no válido, caducado o ya utilizado).
        '503':
          description: No fue posible validar el token Turnstile con Cloudflare.
  /auth/change:
    post:
      tags:
        - Autenticación
      summary: Completar cambio de contraseña
      description: |
        Utilice el `linkURL` completo devuelto por `/auth/firstaccess` o `/auth/forget`, con el `k` en la URL. El enlace contiene los parámetros protegidos de identificación y validez; no los construya manualmente.
        Acepta solo `POST`. Envíe `p` y `z` en el cuerpo del formulario, nunca en la 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 o código requerido por el flujo.
                z:
                  type: string
                  format: password
                  description: Nueva contraseña.
      responses:
        '200':
          description: Contraseña cambiada.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OperationResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          description: El cambio de contraseña fue rechazado.
        '405':
          description: Método distinto de `POST`.
  /auth/logout:
    get:
      tags:
        - Sesión
      summary: Cerrar sesión
      operationId: getLogout
      security:
        - bearerAuth: []
        - sessionCookie: []
      responses:
        '200':
          description: Sesión cerrada.
          headers:
            Set-Cookie:
              $ref: '#/components/headers/ClearedSessionCookie'
        '401':
          $ref: '#/components/responses/Unauthorized'
    post:
      tags:
        - Sesión
      summary: Cerrar sesión
      operationId: postLogout
      security:
        - bearerAuth: []
        - sessionCookie: []
      responses:
        '200':
          description: Sesión cerrada.
          headers:
            Set-Cookie:
              $ref: '#/components/headers/ClearedSessionCookie'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /upload:
    post:
      tags:
        - Transferencias
      summary: Subir archivo
      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: Obligatorio cuando el cuerpo es 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: Archivo recibido.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UploadResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '400':
          $ref: '#/components/responses/BadRequest'
        '403':
          description: "Conexión bloqueada para el usuario: usuario bloqueado, fuera del intervalo horario permitido o rechazada por el programa externo de validación."
  /download/list:
    get:
      tags:
        - Transferencias
      summary: Listar archivos disponibles
      operationId: getDownloadList
      security:
        - bearerAuth: []
        - sessionCookie: []
      parameters:
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Página de archivos disponibles.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FileListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /download/list/backup:
    get:
      tags:
        - Transferencias
      summary: Listar archivos de respaldo
      operationId: getBackupDownloadList
      security:
        - bearerAuth: []
        - sessionCookie: []
      parameters:
        - $ref: '#/components/parameters/Page'
      responses:
        '200':
          description: Página de archivos de respaldo.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FileListResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /download/file:
    get:
      tags:
        - Transferencias
      summary: Descargar archivo listado
      description: |
        Utilice la URL `linkURL` completa del listado, con el token Bearer o la cookie de sesión del mismo usuario que hizo el listado. El enlace solo no da acceso al archivo.
      operationId: getDownloadFile
      security:
        - bearerAuth: []
        - sessionCookie: []
      responses:
        '200':
          description: Contenido del archivo.
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '400':
          description: Credencial ausente.
        '401':
          description: Token inválido o vencido.
        '403':
          description: El token es de otro usuario, la descarga está deshabilitada o el acceso fue bloqueado.
        '404':
          description: Archivo o enlace no encontrado.
  /download/file/backup:
    get:
      tags:
        - Transferencias
      summary: Descargar archivo de respaldo
      description: |
        Utilice la URL `linkURL` completa del listado de respaldo, con el token Bearer o la cookie de sesión del mismo usuario que hizo el listado.
      operationId: getBackupDownloadFile
      security:
        - bearerAuth: []
        - sessionCookie: []
      responses:
        '200':
          description: Contenido del archivo de respaldo.
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '400':
          description: Credencial ausente.
        '401':
          description: Token inválido o vencido.
        '403':
          description: El token es de otro usuario, la descarga está deshabilitada o el acceso fue bloqueado.
        '404':
          description: Archivo o enlace no encontrado.
  /download:
    get:
      tags:
        - Transferencias
      summary: Descargar archivo compartido
      description: |
        Abra la URL segura completa enviada al destinatario. Cuando el enlace requiera un PIN, envíelo como parámetro `p` sin modificar los demás parámetros del enlace.
      operationId: getSharedDownload
      security: []
      parameters:
        - in: query
          name: p
          required: false
          description: PIN requerido por el enlace seguro, cuando corresponda.
          schema:
            type: string
      responses:
        '200':
          description: Contenido del archivo.
          content:
            application/octet-stream:
              schema:
                type: string
                format: binary
        '404':
          description: Archivo o enlace no encontrado.
  /download/pin:
    get:
      tags:
        - Transferencias
      summary: Enviar PIN y obtener enlace de descarga
      description: Utilice la URL segura completa emitida para iniciar el envío del PIN.
      operationId: getDownloadPin
      security: []
      responses:
        '200':
          description: PIN enviado; la respuesta contiene el enlace de descarga.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DownloadPinResponse'
  /report:
    get:
      tags:
        - Informes
      summary: Consultar informe de transferencias
      operationId: getReport
      security:
        - bearerAuth: []
        - sessionCookie: []
      parameters:
        - $ref: '#/components/parameters/Page'
        - in: query
          name: days
          required: false
          description: Período consultado en días; valor predeterminado 1.
          schema:
            type: integer
            default: 1
        - in: query
          name: filterdirection
          required: true
          description: Tipo de transferencia; 5 = descargas concluidas (evento STCPEVTENDSENDFILE), 7 = cargas concluidas (evento STCPEVTENDRECEIVEFILE). Otros valores devuelven 400.
          schema:
            type: integer
            enum:
              - 5
              - 7
        - in: query
          name: filterfile
          required: false
          description: Filtra por nombre de archivo.
          schema:
            type: string
      responses:
        '200':
          description: Registros de transferencia.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReportResponse'
        '401':
          $ref: '#/components/responses/Unauthorized'
  /sso/login:
    get:
      tags:
        - Single Sign-On
      summary: Iniciar sesión federada
      description: Inicia el flujo del navegador y redirige al proveedor.
      operationId: getSsoLogin
      security: []
      parameters:
        - $ref: '#/components/parameters/Provider'
      responses:
        '302':
          description: Redirección al proveedor de identidad.
          headers:
            Location:
              schema:
                type: string
                format: uri
  /sso/logout:
    get:
      tags:
        - Single Sign-On
      summary: Iniciar cierre de sesión federada
      operationId: getSsoLogout
      security:
        - bearerAuth: []
        - sessionCookie: []
      parameters:
        - $ref: '#/components/parameters/Provider'
      responses:
        '302':
          description: Redirección al proveedor o al destino de cierre de sesión.
          headers:
            Location:
              schema:
                type: string
                format: uri
        '401':
          $ref: '#/components/responses/Unauthorized'
  /sso/oauth2/cb:
    get:
      tags:
        - Single Sign-On
      summary: Recibir callback OAuth 2.0 y 2.1/OpenID Connect
      description: Invocado por el proveedor como parte del flujo de inicio de sesión.
      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: Redirección después de procesar el callback.
          headers:
            Location:
              schema:
                type: string
                format: uri
    post:
      tags:
        - Single Sign-On
      summary: Recibir callback OAuth 2.0 y 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: Redirección después de procesar el callback.
          headers:
            Location:
              schema:
                type: string
                format: uri
  /sso/oauth2/logout/cb:
    get:
      tags:
        - Single Sign-On
      summary: Recibir callback de cierre de sesión OAuth 2.0
      operationId: getOauthLogoutCallback
      security: []
      parameters:
        - in: query
          name: state
          required: true
          schema:
            type: string
        - $ref: '#/components/parameters/Provider'
      responses:
        '302':
          description: Redirección después de procesar el callback.
          headers:
            Location:
              schema:
                type: string
                format: uri
    post:
      tags:
        - Single Sign-On
      summary: Recibir callback de cierre de sesión 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: Redirección después de procesar el callback.
          headers:
            Location:
              schema:
                type: string
                format: uri
  /sso/saml2/acs:
    post:
      tags:
        - Single Sign-On
      summary: Recibir aserción SAML
      description: Endpoint ACS invocado por el proveedor SAML mediante HTTP POST.
      operationId: postSamlAcs
      security: []
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              required:
                - SAMLResponse
              properties:
                SAMLResponse:
                  type: string
                  description: Aserción SAML codificada en Base64.
                RelayState:
                  type: string
      responses:
        '302':
          description: Redirección después de procesar la aserción.
          headers:
            Location:
              schema:
                type: string
                format: uri
  /sso/saml2/slo:
    get:
      tags:
        - Single Sign-On
      summary: Recibir mensaje de cierre de sesión 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: Redirección después de procesar el cierre de sesión.
          headers:
            Location:
              schema:
                type: string
                format: uri
    post:
      tags:
        - Single Sign-On
      summary: Recibir mensaje de cierre de sesión 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: Redirección después de procesar el cierre de sesión.
          headers:
            Location:
              schema:
                type: string
                format: uri
  /sso/saml2/metadata:
    get:
      tags:
        - Single Sign-On
      summary: Obtener metadatos SAML mediante query
      operationId: getSamlMetadataByQuery
      security: []
      parameters:
        - $ref: '#/components/parameters/Provider'
      responses:
        '200':
          description: Metadatos XML del proveedor.
          content:
            application/xml:
              schema:
                type: string
  /sso/saml2/metadata/{provider}:
    get:
      tags:
        - Single Sign-On
      summary: Obtener metadatos SAML mediante ruta
      operationId: getSamlMetadataByPath
      security: []
      parameters:
        - name: provider
          in: path
          required: true
          description: Identificador registrado del proveedor SAML.
          schema:
            type: string
      responses:
        '200':
          description: Metadatos XML del proveedor.
          content:
            application/xml:
              schema:
                type: string
components:
  securitySchemes:
    basicAuth:
      type: http
      scheme: basic
    bearerAuth:
      type: http
      scheme: bearer
      description: Token de acceso emitido por un endpoint OAuth.
    sessionCookie:
      type: apiKey
      in: cookie
      name: __Host-dirl_session
      description: Cookie de sesión emitida después del inicio de sesión local o SSO.
  headers:
    SessionCookie:
      description: Cookie de sesión segura devuelta después de la autenticación.
      schema:
        type: string
      example: __Host-dirl_session=<token>; Path=/; Secure; HttpOnly; SameSite=Strict
    ClearedSessionCookie:
      description: Cookie de sesión eliminada cuando corresponda.
      schema:
        type: string
  parameters:
    TurnstileResponse:
      in: header
      name: CF-Turnstile-Response
      required: false
      description: Token generado por el widget Cloudflare Turnstile. Obligatorio cuando `turnstileEnable` está habilitado en la configuración; se ignora en caso contrario. El token es de un solo uso.
      schema:
        type: string
        maxLength: 2048
    JwtIssuer:
      in: header
      name: X-STCPDIRECTLINK-JWT-ISSUER
      required: false
      description: Proveedor utilizado para validar el JWT de origen en token exchange.
      schema:
        type: string
    Username:
      in: query
      name: u
      required: true
      description: Identificación de la cuenta.
      schema:
        type: string
    Page:
      in: query
      name: page
      required: false
      description: Índice de página; valor predeterminado 0.
      schema:
        type: integer
        default: 0
    Provider:
      in: query
      name: provider
      required: true
      description: Identificador del proveedor 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: Solicitud o parámetro no válido.
    Unauthorized:
      description: Credencial o token no aceptado.
    OAuthError:
      description: Error al validar el grant o el token de origen.
      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 del MFA: `1` PIN por correo electrónico o SMS (`/auth/mfa`), `2` TOTP registrado (`/auth/mfa`), `3` registro del 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 completa para completar la etapa.'
        secret:
          type: string
          format: password
          description: 'Solo en el `status` `3`: secreto TOTP en Base32. Vacío en el `status` `2`; ausente en el `status` `1`. Dato sensible.'
        qrcode:
          type: string
          description: 'Solo en el `status` `3`: código QR del secreto, imagen PNG de 200 × 200 en Base64 URL-safe (RFC 4648, con `-` y `_`). Conviértalo a Base64 estándar para usarlo en `data:image/png;base64,`. Vacío en el `status` `2`; ausente en el `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: 'Siempre `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 origen.
        provider:
          type: string
          description: Proveedor que valida el token; tiene prioridad sobre la cabecera 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: Validez del token en segundos; varía según la instancia.
        scope:
          type: string
          description: URI de la API configurada; no representa una lista de permisos.
    OAuthError:
      type: object
      properties:
        error:
          type: integer
          description: Código numérico de la condición notificada.
        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: Destinatario o usuario de destino.
        m:
          type: string
          description: Correo electrónico asociado a la transferencia.
        c:
          type: string
          description: Teléfono móvil asociado a la transferencia.
        h:
          type: string
          description: Hash SHA-256 esperado del archivo.
        n:
          type: string
          description: Identificación o correo electrónico del remitente.
        i:
          type: string
          description: Información adicional de la transferencia.
    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: Tiempo Unix en milisegundos.
        linkURL:
          type: string
          format: uri
          description: Enlace firmado; utilice la URL completa.
    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
