> Pour un agent : la carte de cette documentation est sur https://escouade.io/docs/llms.txt ; chaque page existe en Markdown, à la même adresse suffixée `.md`.

# Meet pour les développeurs

> Meet a une API REST en lecture, des webhooks signés et un serveur MCP. Les clés, les webhooks et les assistants connectés se gèrent dans l'espace de Meet, menu du compte « Développeurs » (`/developpeurs`).

L'API REST, les clés et les webhooks sont **en lecture seule** : rien n'y crée, n'y modifie ni n'y supprime une réunion, un compte rendu ou une action. Une clé ne lit que les réunions dont son propriétaire est propriétaire, hors corbeille ; jamais celles que d'autres membres partagent avec ses équipes.

## Les clés d'API

Une clé se crée dans Développeurs › Clés, avec un nom et au moins une permission. Sa valeur s'affiche **une seule fois**, juste après la création. Elle a la forme `rmk_` suivi d'un identifiant public de huit caractères, d'un `_` et d'un secret ; seule son empreinte est gardée. Elle se révoque à tout moment, définitivement. Un compte a 20 clés actives au plus.

| Permission | Ouvre |
|---|---|
| `meetings:read` | La liste et le détail des réunions : titre, date, état, origine, contexte d'agenda. |
| `reports:read` | Les comptes rendus : synthèse, points clés, décisions, questions ouvertes, chapitres. |
| `actions:read` | Les actions des comptes rendus : responsable, échéance, état « fait ». |
| `transcripts:read` | Les transcriptions, avec une permission à part. |
| `usage:read` | Le temps du mois, par le serveur MCP (outil `get_usage`). |

## L'API REST

Adresse de base : `https://meet.escouade.io/v1`. Authentification : `Authorization: Bearer rmk_…`. Réponses en JSON, dates en RFC 3339 (UTC). Toute méthode autre que `GET` reçoit `405`. L'API n'envoie pas d'en-têtes CORS : elle s'appelle depuis un serveur, jamais depuis une page web, où une clé ne doit pas vivre.

| Ressource | Permission | Rend |
|---|---|---|
| `GET /v1/meetings` | `meetings:read` | `{"data": [réunion…], "next_cursor": "…" ou null}` |
| `GET /v1/meetings/{id}` | `meetings:read` | `{"data": réunion}` |
| `GET /v1/meetings/{id}/report` | `reports:read` | `{"data": compte rendu}`, sans les actions |
| `GET /v1/meetings/{id}/actions` | `actions:read` | `{"data": [action…]}` |
| `GET /v1/meetings/{id}/transcript` | `transcripts:read` | `{"data": {"meeting_id", "segments": […]}}` |

```sh
curl -s https://meet.escouade.io/v1/meetings?limit=5 \
  -H 'Authorization: Bearer rmk_…'
```

### Liste et pagination

`GET /v1/meetings` rend les réunions les plus récentes d'abord. Paramètres : `limit` (1 à 100, 20 par défaut), `cursor` (la valeur `next_cursor` de la page précédente, telle quelle), `status` (un statut ci-dessous). `next_cursor` vaut `null` à la dernière page.

### Une réunion

| Champ | Contenu |
|---|---|
| `id` | Identifiant de la réunion. |
| `title` | Titre. |
| `status` | Statut public (ci-dessous). |
| `starts_at` | Début ; pour un enregistrement du téléphone, son envoi. |
| `scheduled_end_at` | Fin prévue, pour une réunion d'agenda ; sinon `null`. |
| `origin` | `calendar` (agenda connecté), `link` (lien collé), `phone` (enregistreur), `desktop`, `manual` (rendez-vous noté dans Meet). |
| `platform` | `meet`, `teams`, `zoom`, `webex`, `goto`, `in_person`, `desktop`, `other`. |
| `organizer_email` | Organisateur connu de l'agenda, sinon `null`. |
| `context` | Description d'agenda en texte brut (1 200 caractères au plus), sinon `null`. |
| `has_transcript`, `has_report` | Une transcription, un compte rendu existent. |
| `updated_at` | Dernier changement. |
| `participants` | `{"email", "name", "response", "organizer"}`, organisateur en tête ; `response` parmi `accepted`, `tentative`, `declined`, `pending`, `unknown`. |

Jamais : le lien de visio, un chemin de fichier, un identifiant technique interne.

### Les statuts

| `status` | Signification |
|---|---|
| `planned` | Planifiée. |
| `joining` | Le robot rejoint la réunion ou attend en salle d'attente. |
| `recording` | Enregistrement en cours. |
| `processing` | Transcription et compte rendu en préparation. |
| `transcript_ready` | Terminée avec une transcription, sans compte rendu. |
| `report_ready` | Terminée avec un compte rendu. |
| `completed` | Terminée sans transcription ni compte rendu. |
| `failed` | Le traitement n'a pas abouti. |
| `missed` | Manquée. |
| `unsupported` | Plateforme non prise en charge. |

### Compte rendu, actions, transcription

- **Compte rendu** : `meeting_id`, `template`, `summary`, `key_points`, `decisions`, `open_questions` (chacun `{"text", "source_seconds"}`), `chapters` (`{"title", "summary", "source_seconds"}`). Pas encore de compte rendu : `404 not_ready`.
- **Action** : `id` (clé stable), `title`, `assignee`, `due` (texte libre tel que dit, peut être `null`), `completed`, `source_seconds`.
- **Transcription** : `segments` de `{"start", "end", "speaker", "text"}`, en secondes ; `speaker` vaut `null` quand l'intervenant n'est pas connu.

### Erreurs et limites

Corps d'erreur : `{"error": {"code": "…", "message": "…"}}` ; pour un `403`, `required_scope` en plus.

| HTTP | `code` | Quand |
|---|---|---|
| 400 | `invalid_parameter` | `limit` ou `status` hors des valeurs permises. |
| 400 | `invalid_cursor` | Curseur illisible. |
| 401 | `unauthorized` | Clé absente, inconnue ou révoquée. |
| 403 | `insufficient_scope` | Permission manquante. |
| 404 | `not_found` | Réunion inexistante, d'un autre compte ou dans la corbeille. |
| 404 | `not_ready` | Pas encore de compte rendu ou de transcription. |
| 405 | | Méthode autre que `GET`. |
| 429 | `rate_limited` | Plus de 120 appels par minute pour la clé, ou depuis une même adresse IP pour l'API et le MCP réunis. En-tête `Retry-After`. |
| 500 | `internal_error` | Erreur du service. |

## Les webhooks

Les destinations se gèrent dans Développeurs › Webhooks : une adresse **HTTPS** joignable sur Internet, les événements choisis, 10 destinations au plus par compte.

| Événement | Envoyé quand |
|---|---|
| `meeting.transcript_ready` | Une transcription est enregistrée (elle peut être vide si personne n'a parlé). |
| `meeting.report_ready` | Le compte rendu est enregistré. |
| `meeting.processing_failed` | L'enregistrement ou la transcription n'a pas abouti. |

Le corps ne porte que l'essentiel ; le titre et le contenu se lisent ensuite par l'API, avec une clé :

```json
{"id": "evt_…", "type": "meeting.report_ready", "created_at": "2026-10-20T09:41:07Z",
 "data": {"meeting_id": "…", "status": "report_ready"}}
```

### Vérifier la signature

Les en-têtes suivent la convention [Standard Webhooks](https://www.standardwebhooks.com/) :

- `webhook-id` : l'identifiant de l'événement, **le même à chaque nouvel essai** ;
- `webhook-timestamp` : l'instant, en secondes Unix ;
- `webhook-signature` : `v1,` suivi du HMAC-SHA256, en base64, de `{webhook-id}.{webhook-timestamp}.{corps}`, avec pour clé le secret `whsec_…` sans son préfixe, décodé du base64.

Vérifiez en temps constant, refusez un horodatage écarté de plus de 5 minutes, ignorez un `webhook-id` déjà traité. Le secret s'affiche une fois, à la création de la destination.

```python
import base64, hashlib, hmac, time

def verifier(secret, entetes, corps):
    cle = base64.b64decode(secret.removeprefix("whsec_"))
    message = "%s.%s.%s" % (entetes["webhook-id"], entetes["webhook-timestamp"], corps)
    attendu = base64.b64encode(hmac.new(cle, message.encode(), hashlib.sha256).digest()).decode()
    if abs(time.time() - int(entetes["webhook-timestamp"])) > 300:
        return False
    return any(hmac.compare_digest("v1," + attendu, s) for s in entetes["webhook-signature"].split())
```

### La livraison

Votre point d'arrivée répond `2xx` en 10 secondes au plus. Sinon, un nouvel essai part après 30 s, 2 min, 10 min, 30 min, 1 h, 3 h puis 6 h : **8 essais au plus**, puis l'événement est marqué abandonné. Un événement livré ou abandonné se renvoie depuis la page, avec le même identifiant. Aucune redirection n'est suivie. Une destination en pause accumule ses événements, qui partent à la reprise.

## Le serveur MCP

Adresse : **`https://meet.escouade.io/mcp`**, en Streamable HTTP, sans session. Un assistant s'y connecte par OAuth, avec la seule adresse ; un programme, avec une clé `rmk_…` en `Authorization: Bearer`.

Vingt et un outils : onze **lisent**, cinq **préparent** une action et cinq sont réservés aux vues de l'assistant.

| Outil de lecture | Permission | Ce qu'il rend |
|---|---|---|
| `search_meetings` | `meetings:read` | Les réunions qui répondent à une recherche. |
| `get_meeting` | `meetings:read` | Une réunion. |
| `get_report` | `reports:read` | Son compte rendu. |
| `get_action_items` | `actions:read` | Ses actions. |
| `get_transcript` | `transcripts:read` | Sa transcription. |
| `get_today` | `meetings:read` | Les réunions d'un jour, et les actions ouvertes avec `actions:read`. |
| `get_robot_status` | `meetings:read` | Où en est le robot dans une réunion. |
| `list_action_items` | `actions:read` | Les actions des comptes rendus récents, filtrées par responsable ou par état. |
| `search_passages` | `transcripts:read` | Les passages qui répondent à une recherche, par le sens ou par mot exact. |
| `ask_meetings` | `reports:read` et `transcripts:read` | La réponse à une question, avec ses sources ; `found` vaut `false` quand l'information manque. |
| `get_usage` | `usage:read` | Le temps du mois : compris, utilisé, restant. |

### Préparer, puis confirmer

Un assistant connecté par OAuth peut **préparer** une action ; elle n'est faite qu'une fois **confirmée** par la personne, dans la vue de l'assistant ou sur une page de Meet. Jamais par une clé d'API, jamais par l'API REST.

| Outil | Permission | Ce que la confirmation fera |
|---|---|---|
| `send_robot` | `meetings:write` | Envoie le robot dans la visio, maintenant. |
| `stop_robot` | `meetings:write` | Retire le robot de la réunion. |
| `create_meeting` | `meetings:write` | Note un rendez-vous dans Meet, sans lien de visio ni invitation d'agenda. |
| `update_action_item` | `actions:write` | Coche ou décoche une action d'un compte rendu. |
| `share_report` | `reports:share` | Envoie à chaque destinataire un lien de consultation du compte rendu, valable 7 jours. |

Une action préparée attend 10 minutes. Ce qui sort de Meet sans retour possible (un e-mail envoyé, le robot envoyé par un lien collé) ne se confirme que sur la page de Meet. Aucune suppression, aucun paiement, aucun envoi de transcription ou d'enregistrement par ce chemin.

### L'autorisation OAuth

Meet suit la partie autorisation de la spécification MCP : OAuth 2.1, clients publics, PKCE S256 obligatoire, enregistrement dynamique et documents de métadonnées de client. La page de consentement liste les permissions en deux groupes, **Lire** et **Agir** ; seules les quatre lectures des réunions sont cochées d'office, rien de ce qui agit. Le jeton d'accès vaut une heure et se renouvelle ; Développeurs › Serveur MCP liste les assistants connectés, avec « Révoquer ».

### Brancher l'assistant

- **Claude** : Paramètres › Connecteurs › « Ajouter un connecteur personnalisé », nom « Escouade Meet », adresse `https://meet.escouade.io/mcp` ; « Se connecter », puis votre compte Escouade, les permissions, « Autoriser ».
- **ChatGPT** : en mode développeur, un connecteur avec la même adresse et l'authentification OAuth.
- **Claude Code** :

```sh
claude mcp add --transport http escouade-meet https://meet.escouade.io/mcp
```

Un connecteur déjà branché sur `https://robotmeet.fr/mcp` continue de fonctionner ; passer à la nouvelle adresse, c'est le rebrancher, puis révoquer l'ancien.
