A trailing slash was missing in the documentation. Spotted by T. Lemeur when integrating the API.
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."]
|