openapi: 3.0.3
info:
  title: Meet External API
  version: 1.0.0
  description: |
    External API for room management with application-delegated authentication.

    #### Authentication Flow

    1. Exchange application credentials for a JWT token via `/external-api/v1.0/application/token/`.
    2. Use the JWT token in the `Authorization: Bearer <token>` header for all subsequent requests.
    3. Tokens are scoped and allow applications to act on behalf of specific users.

    #### Scopes

    * `rooms:list` – List rooms accessible to the delegated user.
    * `rooms:retrieve` – Retrieve details of a specific room.
    * `rooms:create` – Create new rooms.
    * `rooms:update` – **Coming soon** Update existing rooms, e.g., add attendees to a room.
    * `rooms:delete` – **Coming soon** Delete rooms generated by the application.

    #### Upcoming Features

    * **Add attendees to a room:** You will be able to update a room to include a list of attendees, allowing them to bypass the lobby system automatically.
    * **Delete application-generated rooms:** Rooms created via the application can be deleted when no longer needed.

  contact:
    name: API Support
    email: antoine.lebaud@mail.numerique.gouv.fr

servers:
  - url: https://visio-sandbox.beta.numerique.gouv.fr/external-api/v1.0
    description: Sandbox server

tags:
  - name: Authentication
    description: Application authentication and token generation
  - name: Rooms
    description: Room management operations

paths:
  /application/token/:
    post:
      tags:
        - Authentication
      summary: Generate JWT token
      description: |
        Exchange application credentials for a scoped JWT token that allows the application
        to act on behalf of a specific user.

        The application must be authorized for the user's email domain.
        The returned token expires after a configured duration and must be refreshed by calling this endpoint again.
      operationId: generateToken
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenRequest'
            examples:
              tokenRequest:
                summary: Request token for user delegation
                value:
                  client_id: "550e8400-e29b-41d4-a716-446655440000"
                  client_secret: "1234567890abcdefghijklmnopqrstuvwxyz"
                  grant_type: "client_credentials"
                  scope: "user@example.com"
      responses:
        '200':
          description: Token generated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponse'
              examples:
                tokenResponse:
                  summary: Successful token generation
                  value:
                    access_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
                    token_type: "Bearer"
                    expires_in: 3600
                    scope: "rooms:list rooms:retrieve rooms:create"
        '401':
          description: Authentication failed
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OAuthError'
              examples:
                invalidCredentials:
                  summary: Invalid credentials
                  value:
                    error: "Invalid credentials"
                inactiveApplication:
                  summary: Application is inactive
                  value:
                    error: "Application is inactive"
        '400':
          description: Invalid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OAuthError'
              examples:
                userNotFound:
                  summary: User not found
                  value:
                    error: "User not found."
        '403':
          description: Access denied - cannot delegate user
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OAuthError'
              examples:
                delegationDenied:
                  summary: Domain not authorized
                  value:
                    error: "This application is not authorized for this email domain."

  /rooms:
    get:
      tags:
        - Rooms
      summary: List rooms
      description: |
        Returns a list of rooms accessible to the authenticated user.
        Only rooms where the delegated user has access will be returned.
      operationId: listRooms
      security:
        - BearerAuth: [rooms:list]
      parameters:
        - name: page
          in: query
          description: Page number for pagination
          schema:
            type: integer
            minimum: 1
            default: 1
        - name: page_size
          in: query
          description: Number of items per page
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 20
      responses:
        '200':
          description: List of accessible rooms
          content:
            application/json:
              schema:
                type: object
                properties:
                  count:
                    type: integer
                    description: Total number of rooms
                  next:
                    type: string
                    nullable: true
                    description: URL to next page
                  previous:
                    type: string
                    nullable: true
                    description: URL to previous page
                  results:
                    type: array
                    items:
                      $ref: '#/components/schemas/Room'
              examples:
                roomList:
                  summary: Paginated room list
                  value:
                    count: 2
                    next: "https://visio-sandbox.beta.numerique.gouv.fr/external-api/v1.0/rooms?page=2"
                    previous: null
                    results:
                      - id: "7c9e6679-7425-40de-944b-e07fc1f90ae7"
                        slug: "aae-erez-aaz"
                        access_level: "trusted"
                        url: "https://visio-sandbox.beta.numerique.gouv.fr/aae-erez-aaz"
                        telephony:
                          enabled: true
                          pin_code: "123456"
                          phone_number: "+1-555-0100"
                          default_country: "US"
                        configuration: {}
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'

  /rooms/:
    post:
      tags:
        - Rooms
      summary: Create a room
      description: |
        Creates a new room with secure defaults for external API usage.

        **Defaults:**
        - Delegated user is set as owner
        - Room slug auto-generated for uniqueness
        - Telephony PIN auto-generated when enabled
        - Creation tracked with application client_id for auditing
      operationId: createRoom
      security:
        - BearerAuth: [rooms:create]
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RoomCreate'
            examples:
              emptyBody:
                summary: No parameters (use all defaults)
                value: { }
              withAccessLevel:
                summary: Specify access level
                value:
                  access_level: "trusted"
              withConfiguration:
                summary: Provide room configuration
                value:
                  configuration:
                    everyone_can_mute: true
      responses:
        '201':
          description: Room created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Room'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'

  /rooms/{id}:
    get:
      tags:
        - Rooms
      summary: Retrieve a room
      description: Get detailed information about a specific room by its ID
      operationId: retrieveRoom
      security:
        - BearerAuth: [rooms:retrieve]
      parameters:
        - name: id
          in: path
          required: true
          description: Room UUID
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Room details
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Room'
              examples:
                room:
                  summary: Room details
                  value:
                    id: "7c9e6679-7425-40de-944b-e07fc1f90ae7"
                    slug: "aae-erez-aaz"
                    access_level: "trusted"
                    url: "https://visio-sandbox.beta.numerique.gouv.fr/aae-erez-aaz"
                    telephony:
                      enabled: true
                      pin_code: "123456"
                      phone_number: "+1-555-0100"
                      default_country: "US"
                    configuration: {}
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          $ref: '#/components/responses/ForbiddenError'
        '404':
          $ref: '#/components/responses/RoomNotFoundError'

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
      description: |
        JWT token obtained from the `/application/token/` endpoint.
        Include in requests as: `Authorization: Bearer <token>`

  schemas:
    TokenRequest:
      type: object
      required:
        - client_id
        - client_secret
        - grant_type
        - scope
      properties:
        client_id:
          type: string
          description: Application client identifier
          example: "550e8400-e29b-41d4-a716-446655440000"
        client_secret:
          type: string
          format: password
          writeOnly: true
          description: Application secret key
          example: "1234567890abcdefghijklmnopqrstuvwxyz"
        grant_type:
          type: string
          enum:
            - client_credentials
          description: OAuth2 grant type (must be 'client_credentials')
          example: "client_credentials"
        scope:
          type: string
          format: email
          description: |
            Email address of the user to delegate.
            The application will act on behalf of this user.
            Note: This parameter is named 'scope' to align with OAuth2 conventions,
            but accepts an email address to identify the user. This design allows
            for future extensibility.
          example: "user@example.com"

    TokenResponse:
      type: object
      properties:
        access_token:
          type: string
          description: JWT access token
          example: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJtZWV0LWFwaSIsImF1ZCI6Im1lZXQtY2xpZW50cyIsImlhdCI6MTcwOTQ5MTIwMCwiZXhwIjoxNzA5NDk0ODAwLCJjbGllbnRfaWQiOiI1NTBlODQwMC1lMjliLTQxZDQtYTcxNi00NDY2NTU0NDAwMDAiLCJzY29wZSI6InJvb21zOmxpc3Qgcm9vbXM6cmV0cmlldmUgcm9vbXM6Y3JlYXRlIiwidXNlcl9pZCI6IjdiOGQ5YzQwLTNhMmItNGVkZi04NzFjLTJmM2Q0ZTVmNmE3YiIsImRlbGVnYXRlZCI6dHJ1ZX0.signature"
        token_type:
          type: string
          description: Token type (always 'Bearer')
          example: "Bearer"
        expires_in:
          type: integer
          description: Token lifetime in seconds
          example: 3600
        scope:
          type: string
          description: Space-separated list of granted permission scopes
          example: "rooms:list rooms:retrieve rooms:create"

    RoomCreate:
      type: object
      description: |
        Optional fields for room creation. All fields have secure defaults if omitted.
      properties:
        access_level:
          $ref: '#/components/schemas/RoomAccessLevel'
        configuration:
          $ref: '#/components/schemas/RoomConfiguration'

    RoomConfiguration:
      type: object
      description: |
        Optional room behaviour settings. Unknown fields are rejected.
        All fields are optional and default to `null` (server-side defaults apply).
      properties:
        can_publish_sources:
          type: array
          nullable: true
          description: |
            Restricts which media tracks participants are allowed to publish.
            If `null`, all sources are permitted.
          items:
            type: string
            enum:
              - camera
              - microphone
              - screen_share
              - screen_share_audio
          example: [ "camera", "microphone" ]
        everyone_can_mute:
          type: boolean
          nullable: true
          description: |
            Whether any participant can mute others, or only the room owner/moderator.
            If `null`, the server default applies.
          example: true
      additionalProperties: false

    RoomAccessLevel:
      type: string
      enum:
        - public
        - trusted
        - restricted
      description: |
        Controls who can join the room without going through the lobby.

        - `public`: Anyone with the room link can join directly, no authentication required.
        - `trusted`: Authenticated users join directly. Unauthenticated users wait in the lobby for approval.
        - `restricted`: Only participants explicitly trusted by the owner bypass the lobby. Everyone else waits for approval regardless of authentication.
      example: "trusted"

    Room:
      type: object
      properties:
        id:
          type: string
          format: uuid
          readOnly: true
          description: Unique room identifier
          example: "7c9e6679-7425-40de-944b-e07fc1f90ae7"
        slug:
          type: string
          readOnly: true
          description: URL-friendly room identifier (auto-generated)
          example: "aze-eere-zer"
        access_level:
          $ref: '#/components/schemas/RoomAccessLevel'
        url:
          type: string
          format: uri
          readOnly: true
          description: Full URL to access the room
          example: "https://visio-sandbox.beta.numerique.gouv.fr/aze-eere-zer"
        telephony:
          type: object
          readOnly: true
          description: Telephony dial-in information (if enabled)
          properties:
            enabled:
              type: boolean
              description: Whether telephony is available
              example: true
            pin_code:
              type: string
              description: PIN code for dial-in access
              example: "123456"
            phone_number:
              type: string
              description: Phone number to dial
              example: "+1-555-0100"
            default_country:
              type: string
              description: Default country code
              example: "US"
        configuration:
          $ref: '#/components/schemas/RoomConfiguration'

    OAuthError:
      type: object
      description: OAuth2-compliant error response
      properties:
        error:
          type: string
          description: Human-readable error description
          example: "Invalid credentials"

    Error:
      type: object
      properties:
        detail:
          type: string
          description: Error message
          example: "Invalid token."

    ValidationError:
      type: object
      properties:
        field_name:
          type: array
          items:
            type: string
          description: List of validation errors for this field
          example: ["This field is required."]

  responses:
    UnauthorizedError:
      description: Authentication required or token invalid/expired
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            invalidToken:
              summary: Invalid or expired token
              value:
                error: "Invalid token."
            tokenExpired:
              summary: Token has expired
              value:
                error: "Token expired."

    ForbiddenError:
      description: Insufficient permissions for this operation
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            insufficientScope:
              summary: Missing required scope
              value:
                detail: "Insufficient permissions. Required scope: 'rooms:xxxx'"

    RoomNotFoundError:
      description: Room not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            roomNotFound:
              summary: Room does not exist
              value:
                detail: "Not found."

    BadRequestError:
      description: Invalid request data
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ValidationError'
          examples:
            validationError:
              summary: Field validation failed
              value:
                scope: ["Invalid email address."]
