Allow external platforms using the public API to create provisional users with email-only identification when the user doesn't yet exist in our system. This removes a key friction point blocking third-party integrations from fully provisioning access on behalf of new users. Provisional users are created with email as the primary identifier. Full identity reconciliation (sub assignment) occurs on first login, ensuring reliable user identification is eventually established. While email-only user creation is not ideal from an identity perspective, it provides a pragmatic path to unlock integrations and accelerate adoption through external platforms that are increasingly driving our videoconference tool's growth.
475 lines
15 KiB
YAML
475 lines
15 KiB
YAML
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"
|
||
'401':
|
||
$ref: '#/components/responses/UnauthorizedError'
|
||
'403':
|
||
$ref: '#/components/responses/ForbiddenError'
|
||
|
||
post:
|
||
tags:
|
||
- Rooms
|
||
summary: Create a room
|
||
description: |
|
||
Creates a new room with secure defaults for external API usage.
|
||
|
||
**Restrictions:**
|
||
- Rooms are always created with `trusted` access (no public rooms via API)
|
||
- Room access_level can be updated from the webapp interface.
|
||
|
||
**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 (default)
|
||
value: {}
|
||
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"
|
||
'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: Empty object - all room properties are auto-generated
|
||
properties: {}
|
||
|
||
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:
|
||
type: string
|
||
readOnly: true
|
||
description: Room access level (always 'trusted' for API-created rooms)
|
||
example: "trusted"
|
||
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"
|
||
|
||
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."]
|