openapi: 3.1.0
info:
  title: DrApp Developer Portal API
  version: 1.0.0
  description: |
    API del portal de developers de DrApp. Permite gestionar apps, tokens, webhooks,
    perfil y uso de forma programática.

    ## Autenticación

    Dos métodos soportados:

    1. **API Token** (recomendado para uso programático):
       ```
       Authorization: Bearer devportal_xxx...
       ```
       Creá un token desde [Settings → Tokens API](/settings/tokens).

    2. **Cookie de sesión** (usado por la interfaz web):
       Se obtiene automáticamente al iniciar sesión con magic link.

    ## Scopes

    Los tokens tienen scopes que limitan qué endpoints pueden usar:

    | Scope | Descripción |
    |-------|-------------|
    | `*` | Acceso completo |
    | `apps:read` | Ver apps y sus detalles |
    | `apps:write` | Crear, editar apps |
    | `usage:read` | Ver estadísticas de uso |
    | `profile:read` | Ver perfil |
    | `profile:write` | Editar perfil |
    | `tokens:read` | Listar tokens |
    | `tokens:write` | Crear y revocar tokens |

  contact:
    email: soporte@drapp.com.ar

servers:
  - url: https://developers.drapp.com.ar
    description: Producción
  - url: https://developers.qa.drapp.com.ar
    description: QA

security:
  - BearerAuth: []

paths:
  /api/developer/profile:
    get:
      summary: Obtener perfil
      tags: [Perfil]
      security:
        - BearerAuth: []
      responses:
        '200':
          description: Perfil del developer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Profile'
        '401':
          $ref: '#/components/responses/Unauthorized'
    patch:
      summary: Actualizar perfil
      tags: [Perfil]
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                company:
                  type: string
                developer_type:
                  type: string
                  enum: [empresa_software, clinica_interna, freelancer, investigador]
                country:
                  type: string
                use_case:
                  type: string
                website_url:
                  type: string
                  format: uri
                avatar_url:
                  type: string
                  format: uri
      responses:
        '200':
          description: Perfil actualizado
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Profile'

  /api/developer/notifications:
    get:
      summary: Obtener preferencias de notificación
      tags: [Perfil]
      responses:
        '200':
          description: Preferencias actuales
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotificationPrefs'
    patch:
      summary: Actualizar preferencias de notificación
      tags: [Perfil]
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NotificationPrefs'
      responses:
        '200':
          description: Preferencias actualizadas

  /api/developer/tokens:
    get:
      summary: Listar tokens API
      tags: [Tokens]
      responses:
        '200':
          description: Lista de tokens
          content:
            application/json:
              schema:
                type: object
                properties:
                  tokens:
                    type: array
                    items:
                      $ref: '#/components/schemas/Token'
    post:
      summary: Crear token API
      tags: [Tokens]
      description: |
        Crea un nuevo token de acceso. El token completo se devuelve **una sola vez**
        en el campo `token` de la respuesta. Guardalo de forma segura.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name]
              properties:
                name:
                  type: string
                  maxLength: 100
                  description: Nombre descriptivo del token
                  example: CI/CD Pipeline
                scopes:
                  type: array
                  items:
                    type: string
                    enum: ['*', 'apps:read', 'apps:write', 'usage:read', 'profile:read', 'profile:write', 'sessions:read', 'tokens:read', 'tokens:write']
                  default: ['*']
                  description: Permisos del token
                expires_in_days:
                  type: integer
                  minimum: 1
                  maximum: 365
                  description: Días hasta expiración (null = sin expiración)
      responses:
        '201':
          description: Token creado
          content:
            application/json:
              schema:
                type: object
                properties:
                  token:
                    allOf:
                      - $ref: '#/components/schemas/Token'
                      - type: object
                        properties:
                          token:
                            type: string
                            description: Token completo (solo se muestra una vez)
                            example: devportal_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1
        '400':
          description: Datos inválidos
        '429':
          description: Límite de tokens alcanzado (máx. 20)

  /api/developer/tokens/{id}:
    delete:
      summary: Revocar token
      tags: [Tokens]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Token revocado
          content:
            application/json:
              schema:
                type: object
                properties:
                  ok:
                    type: boolean
        '404':
          description: Token no encontrado

  /api/developer/apps:
    get:
      summary: Listar mis apps
      tags: [Apps]
      responses:
        '200':
          description: Lista de apps del developer
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/App'
    post:
      summary: Registrar nueva app
      tags: [Apps]
      description: |
        Crea una nueva aplicación. Devuelve el `client_id` y `client_secret`.
        El secret se muestra **una sola vez** — guardalo de forma segura.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [name, requested_scopes]
              properties:
                name:
                  type: string
                  description: Nombre de la app
                  example: Mi Integración
                description:
                  type: string
                  nullable: true
                requested_scopes:
                  type: array
                  items:
                    type: string
                  description: Permisos que la app necesita
                  example: [events:read, consumers:read]
                webhook_url:
                  type: string
                  format: uri
                  nullable: true
                website_url:
                  type: string
                  format: uri
                  nullable: true
                contact_email:
                  type: string
                  format: email
                  nullable: true
                post_install_url:
                  type: string
                  format: uri
                  nullable: true
                  description: URL a la que se redirige después de instalar
                tagline:
                  type: string
                  nullable: true
                  description: Descripción corta para el App Store
                description_long:
                  type: string
                  nullable: true
                  description: Descripción detallada (Markdown)
                category:
                  type: string
                  nullable: true
                  enum: [productivity, scheduling, billing, communication, clinical, analytics, integrations, other]
                icon_url:
                  type: string
                  format: uri
                  nullable: true
                screenshots:
                  type: array
                  items:
                    type: string
                    format: uri
                pricing_model:
                  type: string
                  enum: [free, freemium, paid, contact]
                  default: free
                developer_name:
                  type: string
                  nullable: true
                listed:
                  type: boolean
                  default: false
                  description: Publicar en el App Store (requiere aprobación previa)
      responses:
        '201':
          description: App creada
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/App'
                  - type: object
                    properties:
                      client_secret:
                        type: string
                        description: Secret de la app (solo se muestra una vez)

  /api/developer/apps/{id}:
    get:
      summary: Obtener detalle de una app
      tags: [Apps]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Detalle de la app
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/App'
    patch:
      summary: Actualizar app
      tags: [Apps]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                description:
                  type: string
                requested_scopes:
                  type: array
                  items:
                    type: string
                webhook_url:
                  type: string
                website_url:
                  type: string
                contact_email:
                  type: string
                tagline:
                  type: string
                description_long:
                  type: string
                category:
                  type: string
                icon_url:
                  type: string
                screenshots:
                  type: array
                  items:
                    type: string
                listed:
                  type: boolean
      responses:
        '200':
          description: App actualizada
    delete:
      summary: Eliminar app
      tags: [Apps]
      description: Elimina la app y revoca todas sus instalaciones.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: App eliminada
          content:
            application/json:
              schema:
                type: object
                properties:
                  deleted:
                    type: boolean
                  revoked_installations:
                    type: integer

  /api/developer/apps/{id}/secret:
    post:
      summary: Resetear client secret
      tags: [Apps]
      description: Genera un nuevo client_secret. El anterior deja de funcionar.
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Nuevo secret generado
          content:
            application/json:
              schema:
                type: object
                properties:
                  client_secret:
                    type: string

  /api/developer/apps/{id}/installations:
    get:
      summary: Listar instalaciones de una app
      tags: [Apps]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Lista de instalaciones
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Installation'

  /api/developer/apps/{id}/installations/{installId}:
    delete:
      summary: Revocar instalación
      tags: [Apps]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: installId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Instalación revocada

  /api/developer/apps/{id}/webhooks:
    get:
      summary: Listar webhooks de una app
      tags: [Apps]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Lista de webhooks
    post:
      summary: Crear webhook para una app
      tags: [Apps]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url, events]
              properties:
                url:
                  type: string
                  format: uri
                events:
                  type: array
                  items:
                    type: string
      responses:
        '201':
          description: Webhook creado (incluye signing_secret)

  /api/developer/apps/{id}/sandbox:
    post:
      summary: Crear sandbox key
      tags: [Apps]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '201':
          description: Sandbox key creada
    delete:
      summary: Revocar sandbox key
      tags: [Apps]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Sandbox key revocada

  /api/developer/usage:
    get:
      summary: Uso agregado de todas las apps
      tags: [Uso]
      responses:
        '200':
          description: Estadísticas de uso (últimos 30 días)
          content:
            application/json:
              schema:
                type: object
                properties:
                  total_requests:
                    type: integer
                  requests_2xx:
                    type: integer
                  requests_4xx:
                    type: integer
                  requests_5xx:
                    type: integer
                  apps_count:
                    type: integer
                  apps:
                    type: array
                    items:
                      type: object
                      properties:
                        name:
                          type: string
                        client_id:
                          type: string
                        requests:
                          type: integer

  /api/developer/webhooks:
    get:
      summary: Listar webhooks
      tags: [Webhooks]
      responses:
        '200':
          description: Lista de webhooks
          content:
            application/json:
              schema:
                type: object
                properties:
                  webhooks:
                    type: array
                    items:
                      $ref: '#/components/schemas/Webhook'
    post:
      summary: Crear webhook
      tags: [Webhooks]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url, events]
              properties:
                url:
                  type: string
                  format: uri
                  description: URL HTTPS donde recibir eventos
                events:
                  type: array
                  items:
                    type: string
                    enum: [app.approved, app.rejected, app.installed, app.uninstalled, key.rotated]
      responses:
        '201':
          description: Webhook creado (incluye secret para verificar firmas)
          content:
            application/json:
              schema:
                type: object
                properties:
                  webhook:
                    allOf:
                      - $ref: '#/components/schemas/Webhook'
                      - type: object
                        properties:
                          secret:
                            type: string
                            description: Secret para verificar HMAC-SHA256 (solo se muestra una vez)

  /api/developer/webhooks/{id}:
    patch:
      summary: Actualizar webhook
      tags: [Webhooks]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                url:
                  type: string
                  format: uri
                events:
                  type: array
                  items:
                    type: string
                active:
                  type: boolean
      responses:
        '200':
          description: Webhook actualizado
    delete:
      summary: Eliminar webhook
      tags: [Webhooks]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Webhook eliminado

  /api/developer/sessions:
    get:
      summary: Listar sesiones activas
      tags: [Sesiones]
      responses:
        '200':
          description: Lista de sesiones
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Session'
    delete:
      summary: Revocar todas las otras sesiones
      tags: [Sesiones]
      responses:
        '200':
          description: Sesiones revocadas
          content:
            application/json:
              schema:
                type: object
                properties:
                  revoked:
                    type: integer

  /api/developer/sessions/{id}:
    delete:
      summary: Revocar sesión específica
      tags: [Sesiones]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Sesión revocada

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: Token de la forma `devportal_xxx...`

  responses:
    Unauthorized:
      description: No autenticado o token inválido/expirado
      content:
        application/json:
          schema:
            type: object
            properties:
              error:
                type: string
                example: No autenticado

  schemas:
    Profile:
      type: object
      properties:
        email:
          type: string
          format: email
        name:
          type: string
        company:
          type: string
          nullable: true
        developer_type:
          type: string
          enum: [empresa_software, clinica_interna, freelancer, investigador]
        country:
          type: string
          nullable: true
        use_case:
          type: string
          nullable: true
        website_url:
          type: string
          nullable: true
        avatar_url:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time

    NotificationPrefs:
      type: object
      properties:
        notify_app_approved:
          type: boolean
        notify_new_install:
          type: boolean
        notify_weekly_summary:
          type: boolean

    Token:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        prefix:
          type: string
          description: Primeros 20 caracteres del token
          example: devportal_a1b2c3d4e5
        scopes:
          type: array
          items:
            type: string
        expires_at:
          type: string
          format: date-time
          nullable: true
        last_used_at:
          type: string
          format: date-time
          nullable: true
        created_at:
          type: string
          format: date-time
        revoked_at:
          type: string
          format: date-time
          nullable: true

    App:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        client_id:
          type: string
        status:
          type: string
          enum: [pending, approved, rejected, suspended]
        requested_scopes:
          type: array
          items:
            type: string
        created_at:
          type: string
          format: date-time

    Webhook:
      type: object
      properties:
        id:
          type: string
          format: uuid
        url:
          type: string
          format: uri
        events:
          type: array
          items:
            type: string
        active:
          type: boolean
        created_at:
          type: string
          format: date-time

    Installation:
      type: object
      properties:
        id:
          type: string
        team_id:
          type: string
        team_name:
          type: string
          nullable: true
        api_key_prefix:
          type: string
        granted_scopes:
          type: array
          items:
            type: string
        status:
          type: string
          enum: [active, revoked]
        installed_by:
          type: string
        created_at:
          type: string
          format: date-time

    Session:
      type: object
      properties:
        id:
          type: string
          format: uuid
        user_agent:
          type: string
          nullable: true
        ip_address:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time
        expires_at:
          type: string
          format: date-time
        is_current:
          type: boolean
