Docs › Meet
Meet for developers
Meet has a read-only REST API, signed webhooks and an MCP server. Keys, webhooks and connected assistants are managed in Meet’s space, account menu "Développeurs" (/developpeurs).
The REST API, the keys and the webhooks are read-only: nothing there creates, changes or deletes a meeting, minutes or an action. A key only reads the meetings its owner owns, outside the bin; never those other members share with their teams.
API keys
A key is created in Développeurs › Clés, with a name and at least one permission. Its value is shown once only, right after creation. It has the form rmk_ followed by an eight-character public identifier, a _ and a secret; only its fingerprint is kept. It can be revoked at any time, for good. An account has at most 20 active keys.
| Permission | Opens |
|---|---|
meetings:read | The list and details of meetings: title, date, status, origin, calendar context. |
reports:read | Minutes: summary, key points, decisions, open questions, chapters. |
actions:read | The actions in the minutes: owner, deadline, "done" status. |
transcripts:read | Transcriptions, with a permission of their own. |
usage:read | The month’s time, through the MCP server (get_usage tool). |
The REST API
Base address: https://meet.escouade.io/v1. Authentication: Authorization: Bearer rmk_…. Responses in JSON, dates in RFC 3339 (UTC). Any method other than GET receives 405. The API sends no CORS headers: it is called from a server, never from a web page, where a key must not live.
| Resource | Permission | Returns |
|---|---|---|
GET /v1/meetings | meetings:read | {"data": [meeting…], "next_cursor": "…" or null} |
GET /v1/meetings/{id} | meetings:read | {"data": meeting} |
GET /v1/meetings/{id}/report | reports:read | {"data": minutes}, without the actions |
GET /v1/meetings/{id}/actions | actions:read | {"data": [action…]} |
GET /v1/meetings/{id}/transcript | transcripts:read | {"data": {"meeting_id", "segments": […]}} |
curl -s https://meet.escouade.io/v1/meetings?limit=5 \ -H 'Authorization: Bearer rmk_…'
List and pagination
GET /v1/meetings returns the most recent meetings first. Parameters: limit (1 to 100, 20 by default), cursor (the next_cursor value of the previous page, as is), status (a status below). next_cursor is null on the last page.
A meeting
| Field | Content |
|---|---|
id | The meeting’s identifier. |
title | Title. |
status | Public status (below). |
starts_at | Start; for a phone recording, when it was sent. |
scheduled_end_at | Scheduled end, for a calendar meeting; otherwise null. |
origin | calendar (connected calendar), link (pasted link), phone (recorder), desktop, manual (meeting noted in Meet). |
platform | meet, teams, zoom, webex, goto, in_person, desktop, other. |
organizer_email | Organiser known from the calendar, otherwise null. |
context | Calendar description as plain text (1,200 characters at most), otherwise null. |
has_transcript, has_report | A transcription, minutes exist. |
updated_at | Last change. |
participants | {"email", "name", "response", "organizer"}, organiser first; response among accepted, tentative, declined, pending, unknown. |
Never: the video call link, a file path, an internal technical identifier.
Statuses
status | Meaning |
|---|---|
planned | Scheduled. |
joining | The bot is joining the meeting or waiting in the waiting room. |
recording | Recording in progress. |
processing | Transcription and minutes being prepared. |
transcript_ready | Finished with a transcription, without minutes. |
report_ready | Finished with minutes. |
completed | Finished without transcription or minutes. |
failed | Processing did not succeed. |
missed | Missed. |
unsupported | Platform not supported. |
Minutes, actions, transcription
- Minutes:
meeting_id,template,summary,key_points,decisions,open_questions(each{"text", "source_seconds"}),chapters({"title", "summary", "source_seconds"}). No minutes yet:404 not_ready. - Action:
id(stable key),title,assignee,due(free text as spoken, may benull),completed,source_seconds. - Transcription:
segmentsof{"start", "end", "speaker", "text"}, in seconds;speakerisnullwhen the speaker is not known.
Errors and limits
Error body: {"error": {"code": "…", "message": "…"}}; for a 403, required_scope as well.
| HTTP | code | When |
|---|---|---|
| 400 | invalid_parameter | limit or status outside the allowed values. |
| 400 | invalid_cursor | Unreadable cursor. |
| 401 | unauthorized | Key missing, unknown or revoked. |
| 403 | insufficient_scope | Missing permission. |
| 404 | not_found | Meeting that does not exist, belongs to another account or is in the bin. |
| 404 | not_ready | No minutes or transcription yet. |
| 405 | Method other than GET. | |
| 429 | rate_limited | More than 120 calls per minute for the key, or from the same IP address for the API and MCP combined. Retry-After header. |
| 500 | internal_error | Service error. |
Webhooks
Destinations are managed in Développeurs › Webhooks: an HTTPS address reachable on the Internet, the chosen events, at most 10 destinations per account.
| Event | Sent when |
|---|---|
meeting.transcript_ready | A transcription is saved (it may be empty if nobody spoke). |
meeting.report_ready | The minutes are saved. |
meeting.processing_failed | The recording or the transcription did not succeed. |
The body carries only the essentials; the title and content are then read through the API, with a key:
{"id": "evt_…", "type": "meeting.report_ready", "created_at": "2026-10-20T09:41:07Z",
"data": {"meeting_id": "…", "status": "report_ready"}}Verifying the signature
The headers follow the Standard Webhooks convention:
webhook-id: the event’s identifier, the same on every retry;webhook-timestamp: the moment, in Unix seconds;webhook-signature:v1,followed by the HMAC-SHA256, in base64, of{webhook-id}.{webhook-timestamp}.{body}, keyed with thewhsec_…secret without its prefix, decoded from base64.
Verify in constant time, refuse a timestamp more than 5 minutes off, ignore a webhook-id already processed. The secret is shown once, when the destination is created.
import base64, hashlib, hmac, time
def verify(secret, headers, body):
key = base64.b64decode(secret.removeprefix("whsec_"))
message = "%s.%s.%s" % (headers["webhook-id"], headers["webhook-timestamp"], body)
expected = base64.b64encode(hmac.new(key, message.encode(), hashlib.sha256).digest()).decode()
if abs(time.time() - int(headers["webhook-timestamp"])) > 300:
return False
return any(hmac.compare_digest("v1," + expected, s) for s in headers["webhook-signature"].split())Delivery
Your endpoint replies 2xx within 10 seconds. Otherwise, a retry goes out after 30 s, 2 min, 10 min, 30 min, 1 h, 3 h then 6 h: 8 attempts at most, then the event is marked abandoned. A delivered or abandoned event can be resent from the page, with the same identifier. No redirect is followed. A paused destination accumulates its events, which go out when it resumes.
The MCP server
Address: https://meet.escouade.io/mcp, over Streamable HTTP, without sessions. An assistant connects through OAuth, with the address alone; a program, with an rmk_… key in Authorization: Bearer.
Twenty-one tools: eleven read, five prepare an action and five are reserved for the assistant’s views.
| Read tool | Permission | What it returns |
|---|---|---|
search_meetings | meetings:read | The meetings matching a search. |
get_meeting | meetings:read | A meeting. |
get_report | reports:read | Its minutes. |
get_action_items | actions:read | Its actions. |
get_transcript | transcripts:read | Its transcription. |
get_today | meetings:read | A day’s meetings, and the open actions with actions:read. |
get_robot_status | meetings:read | Where the bot stands in a meeting. |
list_action_items | actions:read | The actions of recent minutes, filtered by owner or status. |
search_passages | transcripts:read | The passages matching a search, by meaning or by exact word. |
ask_meetings | reports:read and transcripts:read | The answer to a question, with its sources; found is false when the information is missing. |
get_usage | usage:read | The month’s time: included, used, remaining. |
Prepare, then confirm
An assistant connected through OAuth can prepare an action; it is only carried out once confirmed by the person, in the assistant’s view or on a Meet page. Never through an API key, never through the REST API.
| Tool | Permission | What confirmation will do |
|---|---|---|
send_robot | meetings:write | Sends the bot into the video call, now. |
stop_robot | meetings:write | Removes the bot from the meeting. |
create_meeting | meetings:write | Notes a meeting in Meet, with no video link and no calendar invitation. |
update_action_item | actions:write | Ticks or unticks an action in the minutes. |
share_report | reports:share | Sends each recipient a viewing link to the minutes, valid for 7 days. |
A prepared action waits for 10 minutes. What leaves Meet with no way back (an email sent, the bot sent through a pasted link) can only be confirmed on the Meet page. No deletion, no payment, no sending of a transcription or recording by this route.
OAuth authorisation
Meet follows the authorisation part of the MCP specification: OAuth 2.1, public clients, mandatory PKCE S256, dynamic registration and client metadata documents. The consent page lists permissions in two groups, Read and Act; only the four meeting reads are ticked by default, nothing that acts. The access token lasts an hour and is renewed; Développeurs › Serveur MCP lists the connected assistants, with "Revoke".
Connecting the assistant
- Claude: Settings › Connectors › "Add custom connector", name "Escouade Meet", address
https://meet.escouade.io/mcp; "Connect", then your Escouade account, the permissions, "Allow". - ChatGPT: in developer mode, a connector with the same address and OAuth authentication.
- Claude Code:
claude mcp add --transport http escouade-meet https://meet.escouade.io/mcp
A connector already connected to https://robotmeet.fr/mcp keeps working; moving to the new address means connecting it again, then revoking the old one.