openapi: 3.1.0 info: title: Ulti Suite API version: 1.0.0 description: | API REST Ultimail, UltiDrive et Contacts exposée par **ultid** sous `/api/v1`. ## Authentification Deux modes : | Mode | Header | Usage | |------|--------|-------| | **Session utilisateur** | `Authorization: Bearer ` | Interface web, apps avec login OIDC (Authentik) | | **Token API** | `Authorization: Bearer ulti_` | Agents IA, scripts, intégrations programmatiques | Les tokens API portent des permissions **fine-grained** (lecture/écriture par ressource) et des **scopes** optionnels (comptes mail, dossiers Drive). ## Permissions tokens API Ressources principales : - **Mail** : `mail.mailboxes`, `mail.labels`, `mail.messages`, `mail.search`, `mail.send`, `mail.attachments`, `mail.settings`, `mail.identities`, `mail.automation` - **Drive** : `drive.folders`, `drive.files`, `drive.thumbnails`, `drive.download`, `drive.share`, `drive.upload`, `drive.rename`, `drive.move`, `drive.copy` - **Contacts** : `contacts.read`, `contacts.search`, `contacts.write`, `contacts.delete`, `contacts.labels` - **Automatisations** : `automation.rules`, `automation.webhooks`, `automation.llm`, `automation.search`, `automation.api_tokens` (super admin) Chaque ressource accepte `read` et/ou `write` selon le cas. ## Scopes - **mail_scope** : `{ "all_accounts": true }` ou `{ "all_accounts": false, "account_ids": ["uuid", ...] }` - **drive_scope** : `{ "all_folders": true }` ou `{ "all_folders": false, "folder_paths": ["/Projects", ...] }` servers: - url: /api/v1 description: API ultid (proxifiée par nginx) tags: - name: Tokens API description: Gestion des jetons programmatiques - name: Mail description: Messages, boîtes, envoi - name: Drive description: Fichiers et dossiers Nextcloud - name: Contacts description: Carnet d'adresses - name: Automatisations description: Règles, webhooks, fournisseurs components: securitySchemes: bearerAuth: type: http scheme: bearer bearerFormat: JWT or ulti_token description: JWT OIDC (session) ou token API `ulti_…` schemas: ApiTokenPermissionGrant: type: object required: [resource, read, write] properties: resource: type: string example: mail.messages read: type: boolean write: type: boolean ApiTokenMailScope: type: object properties: all_accounts: type: boolean account_ids: type: array items: type: string format: uuid ApiTokenDriveScope: type: object properties: all_folders: type: boolean folder_paths: type: array items: type: string example: /Projects ApiToken: type: object properties: id: type: string format: uuid name: type: string token_prefix: type: string example: ulti_a3f9b2c1 permissions: type: array items: $ref: '#/components/schemas/ApiTokenPermissionGrant' mail_scope: $ref: '#/components/schemas/ApiTokenMailScope' drive_scope: $ref: '#/components/schemas/ApiTokenDriveScope' created_at: type: string format: date-time last_used_at: type: string format: date-time expires_at: type: string format: date-time ApiTokenCreated: allOf: - $ref: '#/components/schemas/ApiToken' - type: object required: [token] properties: token: type: string description: Secret complet — affiché une seule fois à la création CreateApiTokenRequest: type: object required: [name, permissions, mail_scope, drive_scope] properties: name: type: string permissions: type: array items: $ref: '#/components/schemas/ApiTokenPermissionGrant' mail_scope: $ref: '#/components/schemas/ApiTokenMailScope' drive_scope: $ref: '#/components/schemas/ApiTokenDriveScope' expires_at: type: string format: date-time MessageSummary: type: object properties: id: type: string format: uuid account_id: type: string format: uuid subject: type: string snippet: type: string date: type: string format: date-time DriveFile: type: object properties: path: type: string name: type: string type: type: string enum: [file, directory] size: type: integer mime_type: type: string last_modified: type: string format: date-time Error: type: object properties: error: type: object properties: code: type: string message: type: string security: - bearerAuth: [] paths: /mail/api-tokens: get: tags: [Tokens API] summary: Lister les tokens API description: Nécessite une session OIDC ou un token avec `automation.api_tokens` (écriture). responses: '200': description: Liste des tokens actifs (sans secret) content: application/json: schema: type: object properties: tokens: type: array items: $ref: '#/components/schemas/ApiToken' post: tags: [Tokens API] summary: Créer un token API requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateApiTokenRequest' example: name: Agent tri support permissions: - resource: mail.messages read: true write: false - resource: mail.labels read: true write: true mail_scope: all_accounts: false account_ids: ["550e8400-e29b-41d4-a716-446655440000"] drive_scope: all_folders: true folder_paths: [] responses: '201': description: Token créé — copier le champ `token` immédiatement content: application/json: schema: $ref: '#/components/schemas/ApiTokenCreated' /mail/api-tokens/{tokenID}: delete: tags: [Tokens API] summary: Révoquer un token API parameters: - name: tokenID in: path required: true schema: type: string format: uuid responses: '204': description: Token révoqué /mail/messages: get: tags: [Mail] summary: Lister les messages description: | Permission requise : `mail.messages` (lecture). Scope mail appliqué automatiquement si le token est restreint à certains comptes. parameters: - name: account_id in: query schema: type: string format: uuid - name: folder in: query schema: type: string example: inbox - name: page in: query schema: type: integer - name: page_size in: query schema: type: integer responses: '200': description: Page de messages content: application/json: schema: type: object properties: messages: type: array items: $ref: '#/components/schemas/MessageSummary' /mail/messages/{messageID}: get: tags: [Mail] summary: Lire un message description: Vérifie que le message appartient à un compte autorisé par le token. parameters: - name: messageID in: path required: true schema: type: string format: uuid responses: '200': description: Message complet '403': description: Compte hors scope du token /mail/search: get: tags: [Mail] summary: Rechercher des messages description: Permission `mail.search` (lecture). parameters: - name: q in: query schema: type: string - name: account_id in: query schema: type: string format: uuid - name: from in: query schema: type: string responses: '200': description: Résultats de recherche /mail/send: post: tags: [Mail] summary: Envoyer un message description: Permission `mail.send` (écriture). `account_id` doit être dans le scope. requestBody: required: true content: application/json: schema: type: object required: [account_id, to, subject] properties: account_id: type: string format: uuid to: type: array items: type: string subject: type: string body_html: type: string responses: '200': description: Message envoyé ou mis en file /mail/rules: get: tags: [Automatisations] summary: Lister les règles de tri description: Permission `automation.rules` (lecture). responses: '200': description: Règles post: tags: [Automatisations] summary: Créer une règle description: Permission `automation.rules` (écriture). responses: '201': description: Règle créée /mail/webhooks: get: tags: [Automatisations] summary: Lister les webhooks description: Permission `automation.webhooks` (lecture). responses: '200': description: Webhooks post: tags: [Automatisations] summary: Créer un webhook responses: '201': description: Webhook créé /drive/files/{path}: get: tags: [Drive] summary: Lister un dossier description: Permission `drive.folders` ou `drive.files` (lecture). Path relatif au Drive. parameters: - name: path in: path required: true schema: type: string example: Projects/docs responses: '200': description: Contenu du dossier content: application/json: schema: type: object properties: files: type: array items: $ref: '#/components/schemas/DriveFile' post: tags: [Drive] summary: Uploader un fichier description: Permission `drive.upload` (écriture). Path = dossier cible. parameters: - name: path in: path required: true schema: type: string requestBody: content: multipart/form-data: schema: type: object properties: file: type: string format: binary responses: '201': description: Fichier uploadé /drive/download/{path}: get: tags: [Drive] summary: Télécharger un fichier description: Permission `drive.download` (lecture). parameters: - name: path in: path required: true schema: type: string responses: '200': description: Contenu binaire /drive/preview/{path}: get: tags: [Drive] summary: Miniature / aperçu description: Permission `drive.thumbnails` (lecture). parameters: - name: path in: path required: true schema: type: string responses: '200': description: Image ou flux de prévisualisation /contacts/search: get: tags: [Contacts] summary: Rechercher des contacts description: Permission `contacts.search` (lecture). parameters: - name: q in: query required: true schema: type: string responses: '200': description: Contacts correspondants /contacts/books/{bookID}: get: tags: [Contacts] summary: Lister les contacts d'un carnet description: Permission `contacts.read` (lecture). parameters: - name: bookID in: path required: true schema: type: string responses: '200': description: Contacts du carnet post: tags: [Contacts] summary: Créer un contact description: Permission `contacts.write` (écriture). responses: '201': description: Contact créé /contacts/discovery/llm-settings: get: tags: [Automatisations] summary: Lire les fournisseurs LLM description: Permission `automation.llm` (lecture). responses: '200': description: Configuration LLM put: tags: [Automatisations] summary: Mettre à jour les fournisseurs LLM description: Permission `automation.llm` (écriture). responses: '200': description: Configuration mise à jour /search: get: tags: [Mail, Drive, Contacts] summary: Recherche cross-suite description: | Vérifie les permissions selon `types` : - `mail` → `mail.search` - `drive` → `drive.files` - `contacts` → `contacts.search` parameters: - name: q in: query required: true schema: type: string - name: types in: query schema: type: string example: mail,contacts,drive - name: account_id in: query schema: type: string format: uuid responses: '200': description: Résultats agrégés