# Telnyx Agent Tools: Meeting (Beta) — Full Documentation > Complete page content for Meeting (Beta) (Agent Tools section) of the Telnyx developer docs (https://developers.telnyx.com). > This file: https://developers.telnyx.com/docs/development/llms/agent-tools-meeting-beta-llms-full-txt · Root index: https://developers.telnyx.com/llms.txt ## Meeting (Beta) ### Overview > Source: https://developers.telnyx.com/docs/meeting.md Telnyx Meeting gives your agent a seat in third-party meetings. Point it at a Google Meet, Zoom, Teams, or Webex link and a programmable participant joins the room -- it captures the live transcript, can speak and post to chat, and generates summaries and action items. The virtual participant, aka bot, runs on Telnyx primitives: Telnyx STT for transcription, Telnyx TTS for speaking, and Telnyx Inference for summaries and action items. ## The Workflow Point the bot at a meeting URL. It joins as a visible participant and starts capturing audio. Join now or schedule ahead with `join_at`. While the meeting runs, read the transcript as people speak, plus who joined and what was said in chat. The bot is a participant, not just a recorder -- it can speak back, post to chat, and be interrupted. When the meeting ends, the transcript is finalized and a summary with action items is generated via Telnyx Inference. Read them directly, download recordings, or have them pushed to a webhook. ## Supported Platforms Google Meet, Zoom, Microsoft Teams, and Webex. The platform is detected automatically from the meeting URL. ## Next Steps - [Quick Start](/docs/meeting/quick-start) -- send the bot to a meeting and read the results, four curl commands end to end - [Meeting MCP Server](/docs/meeting/mcp) -- connect an AI agent to 19 dedicated Meeting tools over stateless Streamable HTTP - [Join a Meeting](/docs/meeting/join-meeting) -- create a meeting session and send the bot - [Live Transcript](/docs/meeting/live-transcript) -- follow the transcript stream in real time - [Meeting Presence](/docs/meeting/interact) -- control how the bot appears and acts in the meeting, from its roster name to speaking and chat - [Collect Results](/docs/meeting/collect-results) -- read the finalized transcript and summary - [Webhooks](/docs/meeting/webhooks) -- receive session events on your own endpoint --- ### Quick Start > Source: https://developers.telnyx.com/docs/meeting/quick-start.md Send the bot to a real meeting and get back a transcript, summary, and action items. All you need is a Telnyx API key and a meeting link. Create a meeting session pointing at your meeting URL: ```bash curl -X POST https://api.telnyx.com/v2/meeting_sessions \ -H "Authorization: Bearer $TELNYX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "meeting_url": "https://meet.google.com/abc-defg-hij", "bot_name": "Notetaker", "summarize_on_end": true }' ``` Returns `201 Created` with the session `id`: ```json { "data": { "id": "mtgsess_9b2f...", "status": "joining", "platform": "google_meet", "recording": false } } ``` `summarize_on_end` attempts a `summary` artifact after the transcript is finalized, so a recap is ready without a second request. It does not generate action items -- request an `action_items` artifact separately -- and an empty transcript or a generation failure can result in no completed summary. The bot appears in the meeting lobby under its `bot_name`; admit it like any other participant. Poll the session until `status` is `active`: ```bash curl -H "Authorization: Bearer $TELNYX_API_KEY" \ "https://api.telnyx.com/v2/meeting_sessions/mtgsess_9b2f..." ``` A non-null `joined_at` is positive evidence that the bot became active. `admission_denied` is reserved for an explicit denial by the host; a never-admitted session can also end as plain `ended`, so do not infer attendance from `ended` alone. The full status lifecycle is on [Join with a Meeting URL](/docs/meeting/join-meeting/meeting-url). Long-poll the transcript endpoint while the meeting runs. `after` is your cursor; `wait_seconds` holds the request open until a new segment lands: ```bash curl -H "Authorization: Bearer $TELNYX_API_KEY" \ "https://api.telnyx.com/v2/meeting_sessions/mtgsess_9b2f.../transcript?after=0&wait_seconds=20" ``` ```json { "data": [ { "seq": 41, "text": "let's ship it", "speaker_label": "Ada L.", "confidence": 0.94, "relative_ts": 1240.5, "occurred_at": "2026-06-16T09:00:42Z" } ], "meta": { "next_after": 41 } } ``` Repeat the call with `after` set to `meta.next_after` (the last `seq` you received). When an empty page returns `"next_after": null`, keep the cursor you already had -- null is not a replacement cursor. When the meeting ends -- or you `DELETE` the session to make the bot leave -- the transcript is finalized and the summary generates. Read the artifacts: ```bash curl -H "Authorization: Bearer $TELNYX_API_KEY" \ "https://api.telnyx.com/v2/meeting_sessions/mtgsess_9b2f.../artifacts" ``` Poll until the artifact's `status` is `completed`, then read `content.text` for the recap. To get action items, `POST` to the same `artifacts` endpoint with `{"type": "action_items"}`. The finalized transcript stays available at the `transcript` endpoint. ## Related - [Join a Meeting](/docs/meeting/join-meeting) -- scheduling, calendar auto-join, and the session lifecycle - [Meeting MCP Server](/docs/meeting/mcp) -- run the Meeting workflow from an MCP-compatible AI agent - [Live Transcript](/docs/meeting/live-transcript) -- long polling and the WebSocket stream in depth - [Meeting Presence](/docs/meeting/interact) -- make the bot speak, post to chat, and be interrupted - [Collect Results](/docs/meeting/collect-results) -- artifacts, recordings, and deletion - [Webhooks](/docs/meeting/webhooks) -- push session events to your endpoint instead of polling --- ### MCP Server > Source: https://developers.telnyx.com/docs/meeting/mcp.md The Meeting MCP server gives AI agents a dedicated interface for joining meetings, following transcripts and events, interacting in the room, collecting results, and managing calendar auto-join. ## Endpoint and Authentication Connect to: ```text https://api.telnyx.com/v2/meeting_bot/mcp ``` This is the product-specific Meeting MCP server. It is separate from the generic Telnyx API MCP endpoint at `https://api.telnyx.com/v2/mcp`, which exposes broader API actions. The Meeting endpoint uses stateless Streamable HTTP. Send JSON-RPC 2.0 requests with these headers: ```text Authorization: Bearer Content-Type: application/json Accept: application/json, text/event-stream ``` Every request is independent. The server does not issue an `Mcp-Session-Id`, and clients must not depend on state from an earlier `initialize` request. Responses are currently JSON, while clients must still advertise support for both JSON and server-sent events in `Accept`. ## Configure an MCP Client MCP client configuration formats vary. For clients that accept an `mcpServers` map and environment-variable interpolation, use: ```json { "mcpServers": { "telnyx-meeting": { "url": "https://api.telnyx.com/v2/meeting_bot/mcp", "headers": { "Authorization": "Bearer ${TELNYX_API_KEY}" } } } } ``` Set `TELNYX_API_KEY` in the environment that launches the client. If your client does not interpolate environment variables in JSON, use its secret or header configuration instead of storing an API key in a shared configuration file. ## Discover and Call Tools with curl Export a [Telnyx API key](https://portal.telnyx.com/#/app/api-keys) before running the examples: ```bash export TELNYX_API_KEY='' ``` Initialize the MCP connection: ```bash curl --fail-with-body --silent --show-error \ --request POST \ --url 'https://api.telnyx.com/v2/meeting_bot/mcp' \ --header "Authorization: Bearer $TELNYX_API_KEY" \ --header 'Content-Type: application/json' \ --header 'Accept: application/json, text/event-stream' \ --data '{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "curl", "version": "1.0.0"} } }' ``` Discover the current tool names, descriptions, and input schemas: ```bash curl --fail-with-body --silent --show-error \ --request POST \ --url 'https://api.telnyx.com/v2/meeting_bot/mcp' \ --header "Authorization: Bearer $TELNYX_API_KEY" \ --header 'Content-Type: application/json' \ --header 'Accept: application/json, text/event-stream' \ --data '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' ``` Call the read-only `list_sessions` tool: ```bash curl --fail-with-body --silent --show-error \ --request POST \ --url 'https://api.telnyx.com/v2/meeting_bot/mcp' \ --header "Authorization: Bearer $TELNYX_API_KEY" \ --header 'Content-Type: application/json' \ --header 'Accept: application/json, text/event-stream' \ --data '{ "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": {"name": "list_sessions", "arguments": {}} }' ``` Tool results contain one text content block whose `text` value is JSON. Check `result.isError` before parsing `result.content[0].text`: tool-level failures normally return HTTP `200` with `isError: true`, while authentication and transport failures use HTTP error statuses. JSON-RPC validation failures can instead use the top-level `error` field. ## Available Tools The server currently advertises 19 tools. Use `tools/list` as the source of truth for their current input schemas. ### Meetings and History - `join_meeting` -- create or schedule a meeting session - `get_session` and `list_sessions` -- inspect one session or list sessions - `get_transcript` and `get_events` -- read transcript segments and stored events - `leave_meeting` -- leave or cancel without deleting session history - `get_recordings` -- list session recordings ### In-meeting Actions and Artifacts - `speak`, `stop_speaking`, and `send_chat` -- interact in an active meeting - `create_artifact`, `get_artifact`, and `get_artifacts` -- generate and retrieve summaries or action items ### Calendar Auto-join - `connect_calendar`, `list_calendar_connections`, and `disconnect_calendar` -- manage calendar connections - `set_calendar_policy` -- configure connection-level auto-join behavior - `list_calendar_meetings` -- list meetings from a connected calendar - `set_meeting_auto_join` -- override auto-join for one calendar meeting ## REST-only Capabilities MCP covers the core Meeting workflow but does not expose every REST or streaming operation: - Updating an existing scheduled session is REST-only; MCP has no equivalent of `PATCH /meeting_sessions/{id}`. - Retrieving one calendar connection directly is REST-only; MCP lists connections but has no single-connection retrieval tool. - The customer WebSocket stream is not exposed through MCP. Use the [Live Transcript](/docs/meeting/live-transcript) guide for long polling and WebSocket access. - `join_meeting` does not accept the REST `avatar` configuration. Digital avatar configuration is REST-only. `camera_image` is supported by both REST and MCP. For these operations, use the [Meeting API reference](/api-reference/meeting-sessions/create-a-meeting-session) alongside MCP. ## Related - [Meeting Quick Start](/docs/meeting/quick-start) -- complete the same core workflow with REST - [Join a Meeting](/docs/meeting/join-meeting) -- scheduling, calendar auto-join, and lifecycle details - [Meeting Presence](/docs/meeting/interact) -- speaking, chat, and digital avatars - [Collect Results](/docs/meeting/collect-results) -- transcripts, artifacts, and recordings --- ### Events > Source: https://developers.telnyx.com/docs/meeting/events.md Everything a session does -- joining, admission, speech starting and stopping, chat, recordings, artifacts -- is recorded as an ordered event with a monotonic `seq`. The same events reach you three ways, and they do not carry the same coverage: | | Delivery | Carries | | --- | --- | --- | | `GET /events` | You poll | Every event type below, including after the meeting ends | | [Webhooks](/docs/meeting/webhooks) | Telnyx pushes | Five summary events -- see the table on that page | | [WebSocket stream](/docs/meeting/live-transcript) | Telnyx pushes, live | Every event type, live, plus transcript catch-up | The stored history is the authoritative record. A webhook that never arrived, a socket that dropped mid-meeting, or a process that restarted are all recovered the same way: read from the last `seq` you durably stored. ## Read the Event History `GET /v2/meeting_sessions/{id}/events` returns events in `seq` order. ```bash curl "https://api.telnyx.com/v2/meeting_sessions/mtgsess_9b2f.../events?limit=100" \ -H "Authorization: Bearer $TELNYX_API_KEY" ``` ```json { "data": [ { "seq": 12, "type": "session.status_changed", "payload": { "status": "active", "previous_status": "waiting_for_admission" }, "occurred_at": "2026-06-16T09:00:05Z" } ] } ``` Every event has the same four fields: `seq`, `type`, `payload`, and `occurred_at`. The shape of `payload` is determined by `type`. ### Paging and Resuming Pass `after` with the highest `seq` you have already processed, and `limit` for the page size. Because `seq` is monotonic within a session, the same parameter serves both jobs -- paging through a completed session and resuming after an interruption are the same request. ```bash curl "https://api.telnyx.com/v2/meeting_sessions/mtgsess_9b2f.../events?after=12&limit=100" \ -H "Authorization: Bearer $TELNYX_API_KEY" ``` Store `seq` only after the event is processed. Storing it on receipt means an event lost to a crash is never re-read, and there is no way to detect the gap afterwards. ## Event Types Not every type is delivered by every mechanism -- webhooks carry the five summary events listed on the [Webhooks](/docs/meeting/webhooks) page, while `GET /events` and the stream carry all of them. | Event | Meaning | | --- | --- | | `session.created` | The session record exists; the bot has not necessarily joined. | | `session.status_changed` | Lifecycle moved -- carries `status` and `previous_status`. | | `participant.join` | Someone joined the meeting. | | `participant.leave` | Someone left. | | `participant.speech_on` | A participant started speaking. | | `participant.speech_off` | A participant stopped speaking. | | `transcript.segment` | One transcribed segment. Never delivered by webhook. | | `transcript.completed` | The transcript is finalized -- carries `segment_count` and `last_seq`. | | `bot.speak_requested` | A `speak` action was accepted. | | `bot.speak_stopped` | Speech was stopped, by `stop_speaking` or by barge-in. | | `bot.speak_on_enter_delivered` | The configured `speak_on_enter` line was spoken. | | `chat.message` | A participant posted in the meeting chat. | | `chat.sent` | The bot's own chat message was posted. | | `recording.available` | A recording is ready -- carries `recording_types`, never URLs. | | `artifact.completed` | An artifact finished -- carries `content.text`. | | `artifact.failed` | An artifact could not be generated. | | `avatar.connected` | The avatar media channel attached. | | `avatar.degraded` | The avatar is running in a reduced state. | | `avatar.disconnected` | The avatar media channel dropped. | Treat this list as open. The event type field is deliberately extensible, and a client that rejects an unrecognized `type` will break the first time a new one is added -- ignore what you do not recognize rather than failing. The complete machine-readable enum, with the exact payload schema bound to each type, is in the [Meeting Session agent stream reference](/api-reference/websockets/meeting-session-agent-stream). ## Choosing a Mechanism - **Webhooks** for "tell me when it is finished" -- a summary arrives without you holding a connection, and the five events cover the end states most integrations act on. - **The stream** for anything live: transcript as it is produced, speech starting and stopping, participants arriving. - **`GET /events`** for everything else, and as the backstop for both. It is the only mechanism that answers "what did I miss" after the fact, so a reliable integration reconciles against it even when it is driven by pushes. ## Related - [Webhooks](/docs/meeting/webhooks) -- push delivery, envelope, and signature verification - [Live Transcript](/docs/meeting/live-transcript) -- the WebSocket stream and transcript polling - [Collect Results](/docs/meeting/collect-results) -- the finalized transcript, artifacts, and recordings --- ### API Errors > Source: https://developers.telnyx.com/docs/meeting/errors.md Meeting endpoints return failures as a non-2xx status with a single `error` object -- branch on `error.code` and use `error.message` for diagnostics: ```json { "error": { "code": "not_found", "message": "meeting session not found" } } ``` Authentication, permission, rate-limit, and other request failures that occur before reaching the Meeting service use the [general API error codes](/development/api-fundamentals/api-errors) with the standard plural envelope -- a missing or invalid API key, for example, returns `401` with `{"errors": [{"code": "10009", ...}]}`, not the single-`error` shape below. Authenticated operations can also return `403`, `429` (with `Retry-After` when authentication is overloaded), or `503`. A small number of gateway-level failures (such as a `502` for a meeting URL the provider rejects outright) return a plain-text body with no JSON envelope at all, so parse error responses defensively. ## Meeting Error Codes | Code | HTTP | Title | Detail | Action | | --- | --- | --- | --- | --- | | `unauthorized` | `401` | Invalid or missing credentials | Service-level authentication failure. In practice the gateway rejects a missing or invalid key first with the general `10009` error, so this code is rarely seen. | Send your Telnyx API key as a bearer token in the `Authorization` header (exact `Bearer` capitalization). | | `not_found` | `404` | Meeting session not found | No session with the supplied id belongs to the authenticated account. | Verify the session id and that it was created by the same account. | | `invalid_request` | `400`, `413` | Request rejected | A field in the request is invalid or not allowed, for example a disabled `webhook_url`. A request that exceeds a documented size limit returns the same code with `413`. | Correct the field named in `message` and retry. | | `invalid_state` | `409` | Operation not valid for the session status | The operation requires a different lifecycle status, for example only `scheduled` sessions can be updated and actions require an `active` session. | Read the session status first and retry when the session reaches the required state. | | `unsupported_capability` | `422` | Capability not supported on this platform | The requested capability, such as avatar video, is not available on the detected meeting platform. | Check the platform's capabilities before enabling the feature, or omit it. | | `not_configured` | `503` | Feature not configured | A required integration is not configured for the account. | Complete the feature's setup for your account before retrying. | | `tts_error` | Text-to-speech failure | The speak operation failed in the upstream text-to-speech service. | Retry the speak action; if the failure persists, try a different `voice`. | | `provider_error` | Provider failure | The upstream meeting provider failed, for example a recording-media deletion whose outcome is unknown. | Retry the operation and confirm the result with a follow-up read. | | `auth_unavailable` | Authentication service unavailable | The authentication backend could not be reached. | Retry with backoff. | | `internal_error` | Internal server error | An unexpected failure inside the Meeting service. | Retry with backoff; contact support if the failure persists. | ## Related - [Join a Meeting](/docs/meeting/join-meeting) -- create a meeting session and send the bot - [Meeting Presence](/docs/meeting/interact) -- actions that require an `active` session --- ## Join a Meeting ### Join a Meeting > Source: https://developers.telnyx.com/docs/meeting/join-meeting.md The bot joins your meeting as a visible participant and starts capturing audio. There are two ways to get it into a meeting -- use whichever fits your workflow. ## Choose a Method | | Meeting URL | Calendar auto-join | | --- | --- | --- | | Setup | Create a session pointing at a meeting URL | Connect Google Calendar once | | Effort | Build it yourself with `POST /v2/meeting_sessions` | The bot joins automatically, nothing to build | | Control | Full control over each join, including scheduling | Per-connection policy, session defaults, and per-meeting overrides | | Best for | Custom apps and precise scheduling | Automatically capturing your own meetings | Create a session with the REST API. Full control over each join, including scheduled joins with `join_at`. Connect Google Calendar once and the bot joins your meetings with a link automatically. ## Set a Static Camera Image `POST /v2/meeting_sessions` accepts an optional `camera_image`: a static image shown as the bot's camera tile for this session. It is not a native account or participant profile photo, and its exact presentation can vary by meeting platform and recording configuration. Supply exactly one JPEG source -- inline Base64 or an HTTPS URL: ```json { "camera_image": { "format": "jpeg", "base64_data": "" } } ``` ```json { "camera_image": { "format": "jpeg", "url": "https://cdn.example.com/bot-camera.jpg" } } ``` The image contract is strict: - `format` must be `jpeg`. Supplying both `base64_data` and `url`, or neither, is rejected. - `base64_data` must be canonical plain RFC 4648 Base64 -- no data URIs, whitespace, or URL-safe alphabet. The encoded value is limited to 1,835,008 characters and the decoded JPEG to 1,363,148 bytes; the image itself to 4,096 pixels per dimension and 4 megapixels. - `url` must be a public HTTPS URL of at most 2,048 characters with no credentials, fragment, or explicit non-default port. The service fetches it once before creating the bot, with a five-second timeout, no redirects, and a required `2xx` `image/jpeg` response. Signed query strings are allowed -- treat a signed URL as a credential. - `camera_image` is write-only: neither the image bytes nor the source URL are persisted, returned, or logged. If the session also has a [digital avatar](/docs/meeting/digital-avatars), that live output takes precedence -- the static image is ignored and a URL source is not fetched. --- ### Meeting URL > Source: https://developers.telnyx.com/docs/meeting/join-meeting/meeting-url.md Create a meeting session by pointing the bot at a meeting URL. The bot joins as a visible participant and starts capturing audio. You can join now, schedule the join for a future time, or use [calendar auto-join](/docs/meeting/join-meeting/calendar-auto-join) to have the bot join your meetings automatically. ## Create a Meeting Session ```bash curl -X POST https://api.telnyx.com/v2/meeting_sessions \ -H "Authorization: Bearer $TELNYX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "meeting_url": "https://meet.google.com/abc-defg-hij", "bot_name": "Notetaker", "summarize_on_end": true }' ``` Returns `201 Created`: ```json { "data": { "id": "mtgsess_9b2f...", "status": "joining", "platform": "google_meet", "recording": false } } ``` If you send the same `idempotency_key` again, the request is replayed and you get `200 OK` with the original session instead of a duplicate. The full request schema -- every field, constraint, and default -- lives on [Create a meeting session](/api-reference/meeting-sessions/create-a-meeting-session) in the API Reference. ### Platform Detection The platform is detected automatically from the meeting URL: | URL pattern | Platform | | --- | --- | | `meet.google.com/...` | `google_meet` | | `*.zoom.us/...` | `zoom` | | `teams.microsoft.com/...` | `teams` | | `teams.live.com/...` | `teams` | | `*.webex.com/...` | `webex` | During the beta, creating a session with a `teams.microsoft.com` URL can fail with a gateway `502` while Teams support rolls out; `teams.live.com` URLs, Google Meet, Zoom, and Webex URLs create sessions normally. ## Session Statuses A session moves from `joining` to `waiting_for_admission` when the bot reaches the lobby, and to `active` once it enters the room -- `active`, and a non-null `joined_at`, are the positive evidence that the bot got in. It finishes as `ended`, `failed`, or `admission_denied`. `admission_denied` is reserved for an explicit denial by the host; a session that was cancelled or timed out without being admitted ends as `ended` with `joined_at: null`, so ending alone does not prove attendance. Scheduled sessions wait in `scheduled` until `join_at`. Every status and its meaning is documented on [Retrieve a meeting session](/api-reference/meeting-sessions/retrieve-a-meeting-session). ## Schedule a Join Set `join_at` to a future timestamp. The bot joins when the meeting starts: ```bash curl -X POST https://api.telnyx.com/v2/meeting_sessions \ -H "Authorization: Bearer $TELNYX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "meeting_url": "https://meet.google.com/abc-defg-hij", "join_at": "2026-08-05T16:00:00Z" }' ``` While a session is `scheduled`, update it with `PATCH`: ```bash curl -X PATCH https://api.telnyx.com/v2/meeting_sessions/mtgsess_9b2f... \ -H "Authorization: Bearer $TELNYX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "join_at": "2026-08-05T17:00:00Z" }' ``` Only `join_at` and `bot_name` can be updated, and only while the session is `scheduled`. ## Leave or Cancel a Session `DELETE` ends the bot's participation. It does not delete the session history, transcript, or artifacts: ```bash curl -X DELETE https://api.telnyx.com/v2/meeting_sessions/mtgsess_9b2f... \ -H "Authorization: Bearer $TELNYX_API_KEY" ``` ## Errors Failures return a non-2xx status with an error envelope: ```json { "error": { "code": "not_found", "message": "meeting session not found" } } ``` ## Related - [Calendar Auto-join](/docs/meeting/join-meeting/calendar-auto-join) -- the bot joins your meetings automatically - [Live Transcript](/docs/meeting/live-transcript) -- follow the transcript stream in real time - [Meeting Presence](/docs/meeting/interact) -- control how the bot appears and acts in the meeting - [Collect Results](/docs/meeting/collect-results) -- read the finalized transcript and summary --- ### Calendar Auto-join > Source: https://developers.telnyx.com/docs/meeting/join-meeting/calendar-auto-join.md Connect Google Calendar once and the bot attends your meetings automatically: Meeting schedules a bot for eligible calendar events that have a supported join link. Each auto-joined meeting becomes a normal session with the same [live transcript](/docs/meeting/live-transcript), [recordings and results](/docs/meeting/collect-results), and [webhooks](/docs/meeting/webhooks). Outlook is reserved for a future release. ## Calendar API The Calendar API includes eight routes: | Method | Route | Purpose | | --- | --- | --- | | `POST` | `/v2/calendar_connections` | Start Google OAuth and return `pending_oauth` plus `oauth_url`; no connection row exists until the callback succeeds. | | `GET` | `/v2/calendar_connections` | List connections owned by the authenticated connecting user. | | `GET` | `/v2/calendar_connections/{id}` | Retrieve one connection owned by that user. | | `PATCH` | `/v2/calendar_connections/{id}` | Update policy, bot name, or future-session defaults. | | `DELETE` | `/v2/calendar_connections/{id}` | Disconnect and stop future joins for that connection. | | `GET` | `/v2/calendar_connections/{id}/meetings` | List tracked meetings; defaults to the next seven days and accepts an ISO-8601 `from`/`to` window up to 31 days. | | `PATCH` | `/v2/calendar_connections/{id}/meetings/{meeting_id}` | Set `bot_will_join` to `true`, `false`, or `null` (follow policy). | | `GET` | `/v2/meeting_bot/calendar/oauth_callback` | Browser OAuth callback. Identity comes from verified, short-lived signed state; clients do not call it with bearer auth. | ## Connect Your Calendar ```bash curl -X POST https://api.telnyx.com/v2/calendar_connections \ -H "Authorization: Bearer $TELNYX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "platform": "google_calendar" }' ``` ```json { "data": { "platform": "google_calendar", "status": "pending_oauth", "oauth_url": "https://accounts.google.com/o/oauth2/v2/auth?..." } } ``` Open `oauth_url` in a browser and grant read-only calendar access. After the callback succeeds, list the connection to obtain its `id` and connected status. ## Configure Auto-join ```bash curl -X PATCH https://api.telnyx.com/v2/calendar_connections/calconn_... \ -H "Authorization: Bearer $TELNYX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "auto_join_policy": "external_organizer_excluded", "bot_name": "Calendar Notetaker", "default_barge_in": false, "default_summarize_on_end": true, "default_voice": null }' ``` | Policy | Behavior | | --- | --- | | `all_meetings` | Join eligible events that have a supported meeting link. | | `external_organizer_excluded` | Skip Google events where `organizer.self` is not `true`. This means the connected calendar user did not organize the event; it is not a domain comparison, so a coworker's same-domain event is also excluded. | | `per_meeting` | Join only meetings explicitly opted in with the per-meeting PATCH route. | Connection defaults apply to sessions adopted after the change; they do not rewrite already-created sessions. `bot_will_join` in a meeting response is the effective decision under policy, while `bot_will_join_override` is the explicit nullable override. ## Timing, Ownership, and Identity Retention Calendar scheduling applies the configured join lead time. If `event.start_time - lead_time` is still in the future, the bot starts joining then; if that lead window has elapsed, it starts at the event start time rather than using a past `join_at`. Calendar connections, settings, and tracked-meeting controls are owned by the user who connected the calendar. Sessions generated from those meetings, and their transcripts, recordings, events, artifacts, and usage data, are organization-level Meeting resources visible to other authenticated users or API keys in the same Telnyx organization. Organizer and attendee identities are retained only for a configured post-event window (currently 24 hours by default) and are then scrubbed from Calendar meeting records. Disconnecting also erases those Calendar identity fields. This identity-retention boundary does not delete the separately retained organization-level Meeting session resources. ## Related - [Join with a meeting URL](/docs/meeting/join-meeting/meeting-url) -- create sessions through the API - [Live Transcript](/docs/meeting/live-transcript) -- follow the transcript stream in real time - [Collect Results](/docs/meeting/collect-results) -- read the finalized transcript and summary --- ## During the Meeting ### Live Transcript > Source: https://developers.telnyx.com/docs/meeting/live-transcript.md While the meeting runs, the bot captures audio and Telnyx STT transcribes it in real time. You have two ways to read segments as they are produced: a WebSocket stream (alpha) or incremental long polling. ## Read the Transcript Incrementally `GET /v2/meeting_sessions/{id}/transcript` returns segments in ascending `seq` order, at most `limit` per page (default 100, maximum 1,000). Use `after` to page forward and `wait_seconds` to hold the request open (up to 25 seconds) so new segments arrive as soon as they are produced: ```bash curl -H "Authorization: Bearer $TELNYX_API_KEY" \ "https://api.telnyx.com/v2/meeting_sessions/mtgsess_9b2f.../transcript?after=40&wait_seconds=20" ``` ```json { "data": [ { "seq": 41, "text": "let's ship it", "speaker_label": "Ada L.", "confidence": 0.94, "relative_ts": 1240.5, "occurred_at": "2026-06-16T09:00:42Z" } ], "meta": { "next_after": 41 } } ``` `wait_seconds` turns the call into a long poll: if no new segments exist yet, the request waits and returns as soon as a new segment lands or the timeout elapses (an empty `data` array with `"next_after": null`). Pass `meta.next_after` as the next request's `after` cursor; on an empty page keep the cursor you already had rather than replacing it with null. ## Connect to the WebSocket Stream The stream endpoint replays stored events and transcript segments, then follows the meeting live. Open a connection with your API key on the upgrade: ``` WSS /v2/meeting_sessions/{id}/stream?after_seq=0&after_transcript_seq=0 ``` Send your API key in the `Authorization` header using the Bearer scheme on the upgrade request. Header auth is the only supported mechanism -- query-string credentials such as an `access_token` parameter are rejected with `401`. Browser-native WebSocket clients cannot set the `Authorization` header during the handshake, so connect from a backend and relay to the browser if needed. A successful upgrade returns `101`. Before upgrade, the server can return `401` for missing or invalid credentials or a nonexistent, foreign, or already-terminal session; `403` for a forbidden credential; `429` with `Retry-After` when authentication is overloaded; or `503` while authentication is unavailable. This is a WebSocket upgrade endpoint, not an ordinary HTTP `GET` route. A plain `curl` request -- or a request with only partial upgrade headers -- can reach the Meeting service and return an HTML `Cannot GET` response even when the WebSocket route is available. Use a WebSocket client that performs the complete handshake; verify a successful connection by the `101 Switching Protocols` response and incoming frames. For example, connect from a backend terminal with [`wscat`](https://github.com/websockets/wscat). The Meeting Session must belong to the API-key account and must not be in a terminal state: ```bash export SESSION_ID='mtgsess_...' npx --yes wscat \ --connect "wss://api.telnyx.com/v2/meeting_sessions/${SESSION_ID}/stream?after_seq=0&after_transcript_seq=0" \ --header "Authorization: Bearer [REDACTED]" ``` A successful connection prints `Connected (press CTRL+C to quit)` and begins receiving JSON frames. To test bidirectional commands while the session status is `active`, enter a command such as: ```json { "action": "send_chat", "text": "WebSocket connection verified." } ``` The meeting receives the chat message and the socket receives the corresponding `chat.sent` event. Press Ctrl+C to disconnect. Set `after_seq` and `after_transcript_seq` to the last sequence numbers you have seen. The server replays stored records after those cursors, then switches to live delivery. Replay is bounded: each connection replays at most 1,000 event rows and 1,000 transcript rows, and a `{"kind": "truncated", "store": "events" | "transcript"}` frame signals that more rows remain -- reconnect with that store's cursor set to the last sequence received, or backfill through the REST endpoints. Catch-up and live delivery can overlap, so tolerate duplicates; catch-up event frames carry a durable top-level `seq` while live event frames may not, and a sequence-less live frame must not advance your durable cursor. Transcript segments arrive in two shapes, and a client has to handle both. Segments **replayed from the transcript store** during catch-up arrive as standalone `transcript` frames: ```json { "kind": "transcript", "seq": 17, "text": "Hello, can everyone hear me?", "confidence": 0.98, "speaker_label": "Alice", "relative_ts": 12.4, "occurred_at": "2026-06-16T09:00:12Z" } ``` Segments produced **live** during the meeting arrive instead as event frames, with the segment nested under `payload`: ```json { "kind": "event", "type": "transcript.segment", "payload": { "seq": 18, "text": "Yes, loud and clear.", "confidence": 0.97, "speaker_label": "Bob", "relative_ts": 14.9 }, "occurred_at": "2026-06-16T09:00:14Z" } ``` A client that branches only on `kind == "transcript"` receives nothing while a meeting is live -- silently, with no error -- because every segment produced after connection is an event frame. Other frame kinds (`event`, `error`, `truncated`) and the complete frame protocol -- payload schemas, commands, and close codes -- are specified in the [Meeting Session agent stream reference](/api-reference/websockets/meeting-session-agent-stream). Segment fields (`seq`, `text`, `speaker_label`, `confidence`, `relative_ts`, `occurred_at`) are documented on [List meeting session transcript](/api-reference/meeting-session-data/list-meeting-session-transcript). ### Sending Commands over the Stream You can make the bot act from the same socket. Send a JSON frame: ```json { "action": "speak", "text": "Let's recap what we agreed on.", "interrupt": false } ``` ```json { "action": "send_chat", "text": "Noted. I will send the summary after the call." } ``` The stream allows at most 8 commands in flight or queued, and there is no per-command acknowledgement. Inbound frames larger than 32 KiB close the connection with code `1009`, and if the server's outbound buffer backs up it closes with code `1013` and reason `backpressure` -- reconnect with your last durable cursors. For durable, acknowledged control prefer the [REST actions](/docs/meeting/interact). ### Availability The WebSocket stream is currently an **alpha** feature and is not covered by a stability or availability commitment. The long-polling `transcript` endpoint above works on the standard API and is the supported way to read the transcript today. ## Related - [Session Events](/docs/meeting/events) -- the stored event history behind the stream - [Join a Meeting](/docs/meeting/join-meeting) -- create a meeting session and send the bot - [Meeting Presence](/docs/meeting/interact) -- control how the bot appears and acts in the meeting, from its roster name to speaking and chat - [Collect Results](/docs/meeting/collect-results) -- read the finalized transcript and summary --- ### Meeting Presence > Source: https://developers.telnyx.com/docs/meeting/interact.md The bot is a participant, not just a recorder. While the meeting is `active`, drive it through the session actions: speak, stop speaking, and post to chat. All three return `202 Accepted` and complete asynchronously. The bot has two presence modes. **Bot presence** is the default plain roster entry, covered on this page. [**Digital avatars**](/docs/meeting/digital-avatars) give the bot a rendered avatar on camera. ## Bot Presence The bot joins as a visible participant with its own entry in the meeting roster. By default it appears as **Meeting Bot**; set `bot_name` (1-100 characters) when you create the session to control the name other attendees see: ```bash curl -X POST https://api.telnyx.com/v2/meeting_sessions \ -H "Authorization: Bearer $TELNYX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "meeting_url": "https://meet.google.com/abc-defg-hij", "bot_name": "Q3 Decisions Scribe" }' ``` You can also rename the bot before it joins with `PATCH /v2/meeting_sessions/{id}` while the session is `scheduled`. There is no API for a custom profile picture. The bot uses the meeting platform's default profile for its account, so its roster entry is the name above plus the platform's standard bot appearance. ## Digital Avatars Instead of a plain roster entry, give the bot a rendered digital avatar as its in-meeting presence. The digital avatar replaces the bot's camera: attendees see it on screen, and everything the bot says through [speak](#speak) is lip-synced by the digital avatar. See [Digital avatars](/docs/meeting/digital-avatars) for the `avatar` parameter, provider requirements, and `avatar_state`. ## Speak `POST /v2/meeting_sessions/{id}/actions/speak` makes the bot speak text into the meeting. The session's `voice` is the default; override it per request. ```bash curl -X POST https://api.telnyx.com/v2/meeting_sessions/mtgsess_9b2f.../actions/speak \ -H "Authorization: Bearer $TELNYX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "text": "Here are the three decisions from this call.", "interrupt": false }' ``` The full request schema is on [Speak in a meeting session](/api-reference/meeting-session-actions/speak-in-a-meeting-session). Speech is queued, so a long utterance is never cut short by the next `speak` call. If `barge_in` was enabled on the session, participants can interrupt the bot with their own speech. To have the bot speak as soon as it is admitted without a `speak` call, set `config.speak_on_enter` to the text it should say when creating the session. It sits alongside `voice` and `barge_in` and applies to every session, independent of any assistant. ## Stop Speaking `POST /v2/meeting_sessions/{id}/actions/stop_speaking` stops the current utterance and flushes the queue. ```bash curl -X POST https://api.telnyx.com/v2/meeting_sessions/mtgsess_9b2f.../actions/stop_speaking \ -H "Authorization: Bearer $TELNYX_API_KEY" ``` ## Send a Chat Message `POST /v2/meeting_sessions/{id}/actions/send_chat` posts a message to the meeting's native chat as the bot. ```bash curl -X POST https://api.telnyx.com/v2/meeting_sessions/mtgsess_9b2f.../actions/send_chat \ -H "Authorization: Bearer $TELNYX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "text": "I will send the summary after this call." }' ``` Native chat is supported on Google Meet, Zoom, and Teams. Webex does not support native chat, although joining and audio/video output remain supported. Chat is two-way: messages other participants post are delivered as `chat.message` events, while the bot's own posts echo back as `chat.sent`. Subscribe on the WebSocket stream or a webhook to read inbound chat. The full request schema is on [Send chat in a meeting session](/api-reference/meeting-session-actions/send-chat-in-a-meeting-session). ## Same Commands over WebSocket The same three actions can be sent as frames on the [live transcript stream](/docs/meeting/live-transcript) instead of over REST. The stream is convenient for low-latency bots but provides no per-command acknowledgement and caps queued commands at 8. Prefer REST actions when you need a durable, acknowledged response. ## Errors Action requests fail with a non-2xx status and an error envelope, for example when the session is not `active`: ```json { "error": { "code": "invalid_state", "message": "session must be active to perform this action (current status: ended)" } } ``` ## Related - [Digital avatars](/docs/meeting/digital-avatars) -- render the bot as a speaking avatar on camera - [Join a Meeting](/docs/meeting/join-meeting) -- create a meeting session and send the bot - [Live Transcript](/docs/meeting/live-transcript) -- follow the transcript stream in real time - [Collect Results](/docs/meeting/collect-results) -- read the finalized transcript and summary --- ### Digital Avatars > Source: https://developers.telnyx.com/docs/meeting/digital-avatars.md Instead of a plain roster entry, give the bot a rendered digital avatar as its in-meeting presence. The digital avatar replaces the bot's camera: attendees see it on screen, and everything the bot says through [speak](/docs/meeting/interact#speak) is lip-synced by the digital avatar. This suits branded, customer-facing, or demo meetings where a personified presence matters. Enable it with the `avatar` parameter at session creation. Build the JSON with `jq --arg` so the shell expands your Anam key -- inside a single-quoted `-d '...'` body, `$ANAM_API_KEY` would be sent literally: ```bash REQUEST_BODY=$(jq -n \ --arg meeting_url "https://meet.google.com/abc-defg-hij" \ --arg avatar_id "your-anam-avatar-id" \ --arg anam_api_key "$ANAM_API_KEY" \ '{ meeting_url: $meeting_url, avatar: { provider: "anam", avatar_id: $avatar_id, api_key: $anam_api_key } }') curl -X POST https://api.telnyx.com/v2/meeting_sessions \ -H "Authorization: Bearer $TELNYX_API_KEY" \ -H "Content-Type: application/json" \ --data "$REQUEST_BODY" ``` | Field | Type | Description | | --- | --- | --- | | `provider` | string | The avatar provider. Today this is `anam`. | | `avatar_id` | string | The digital avatar to use, chosen from your anam account. | | `api_key` | string | Your anam API key. Write-only: it mints a session token and is never stored or returned. Keep it in a secret, not in committed code. | How it works: - Telnyx TTS drives the digital avatar's speech and lip sync. - Avatar sessions are immediate REST creates only: `join_at` is not supported, and avatars are not available through calendar auto-join. Avatar mode is chosen at creation and cannot be toggled mid-meeting. - The digital avatar works on all supported platforms (Google Meet, Zoom, Teams, Webex). - Readiness is visible in the session response via `avatar_state`: `starting`, `connected`, `degraded`, or `disconnected` -- read it rather than assuming the camera is ready. - The avatar's camera video is not included in the provider recording; the bot's audio is. - An active avatar takes precedence over a [static `camera_image`](/docs/meeting/join-meeting#set-a-static-camera-image): the image is ignored and its URL is not fetched. The avatar provider is pluggable. `anam` is the only provider available today; additional providers, including a Telnyx-hosted digital avatar, are under evaluation for future releases. ## Related - [Meeting Presence](/docs/meeting/interact) -- the default bot presence and session actions --- ## After the Meeting ### Collect Results > Source: https://developers.telnyx.com/docs/meeting/collect-results.md When the meeting ends, the transcript is finalized and stored. If `summarize_on_end` was set to `true` on session creation, a `summary` artifact is generated via Telnyx Inference (action items are a separate artifact type you request yourself). Read results directly, or have them pushed to a webhook. A terminal `ended` status alone does not prove the bot attended -- check that `joined_at` is non-null when attendance matters. ## Get the Transcript `GET /v2/meeting_sessions/{id}/transcript` returns the finalized transcript, cursor-paged: with no query parameters it returns at most the first 100 segments, not the entire transcript. Page with `after` and `limit` (1-1,000) until a request comes back with empty `data`: Cursor and long-poll query parameters (`after`, `limit`, `wait_seconds`) are documented on [List meeting session transcript](/api-reference/meeting-session-data/list-meeting-session-transcript). ```bash curl -H "Authorization: Bearer $TELNYX_API_KEY" \ "https://api.telnyx.com/v2/meeting_sessions/mtgsess_9b2f.../transcript?after=0&limit=1000" ``` ```json { "data": [ { "seq": 41, "text": "let's ship it", "speaker_label": "Ada L.", "confidence": 0.94, "relative_ts": 1240.5, "occurred_at": "2026-06-16T09:00:42Z" } ], "meta": { "next_after": 41 } } ``` The `transcript.completed` webhook fires when the transcript is finalized. See [Webhooks](/docs/meeting/webhooks). ## Artifacts Artifacts are generated outputs with type `summary` or `action_items`; each type requires its own request (`summarize_on_end` attempts only a `summary`). Trigger one, then poll until it is `completed` and read the generated text from `content.text`. An artifact can be requested any time the transcript has content -- including while the meeting is still running; a session with an empty transcript returns `409 invalid_state`. ### Create an Artifact `POST /v2/meeting_sessions/{id}/artifacts` returns `202 Accepted`. The request is not idempotent -- each call generates a new artifact. ```bash curl -X POST https://api.telnyx.com/v2/meeting_sessions/mtgsess_9b2f.../artifacts \ -H "Authorization: Bearer $TELNYX_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "type": "summary" }' ``` `type` is `summary` (decisions, recap) or `action_items` (owned tasks); the full request schema is on [Create a meeting session artifact](/api-reference/meeting-session-artifacts/create-a-meeting-session-artifact). ### List Artifacts ```bash curl -H "Authorization: Bearer $TELNYX_API_KEY" \ "https://api.telnyx.com/v2/meeting_sessions/mtgsess_9b2f.../artifacts" ``` ### Get an Artifact ```bash curl -H "Authorization: Bearer $TELNYX_API_KEY" \ "https://api.telnyx.com/v2/meeting_sessions/mtgsess_9b2f.../artifacts/mtgart_..." ``` ```json { "data": { "id": "mtgart_4e7c...", "session_id": "mtgsess_9b2f...", "type": "summary", "status": "completed", "content": { "text": "Decisions: ... Actions: ..." }, "model_provenance": { "model": "example-model", "provider": "telnyx" }, "failure_reason": null, "created_at": "2026-06-16T09:05:10Z", "updated_at": "2026-06-16T09:05:16Z" } } ``` Poll the artifact until `status` is `completed` or `failed`. A `failed` artifact includes a `failure_reason`. The `artifact.completed` and `artifact.failed` webhooks also fire. Generation currently reads at most the first 10,000 transcript segments, so an exceptionally long meeting can produce an incomplete artifact, and model context limits can still cause a failure. An automatic `summarize_on_end` attempt that is skipped (for example, on an empty transcript) creates no artifact row at all. ## Recordings If the session records, `GET /v2/meeting_sessions/{id}/recordings` returns the available recordings with short-lived download URLs: ```bash curl -H "Authorization: Bearer $TELNYX_API_KEY" \ "https://api.telnyx.com/v2/meeting_sessions/mtgsess_9b2f.../recordings" ``` ```json { "data": [ { "type": "video_mixed", "url": "https://...", "expires_at": null } ] } ``` The full response schema is on [List meeting session recordings](/api-reference/meeting-session-data/list-meeting-session-recordings). `url` is a short-lived signed link on the recording provider's storage; re-fetch this endpoint whenever you need a current URL rather than treating it as durable storage. The current adapter returns `expires_at: null` -- the API does not expose the URL's actual expiry, and the link still expires. The `recording.available` webhook fires when a recording is ready; it carries only `recording_types` and never URLs, so always fetch them from this endpoint. ### Delete Recording Media This endpoint is not yet available in production; calling it currently returns a generic `404`. It is documented ahead of rollout. `DELETE /v2/meeting_sessions/{id}/recording_media` permanently deletes the provider-hosted recording media for the session (recordings, audio, video, and debug media). This is irreversible and returns `202 Accepted`: ```bash curl -X DELETE https://api.telnyx.com/v2/meeting_sessions/mtgsess_9b2f.../recording_media \ -H "Authorization: Bearer $TELNYX_API_KEY" ``` ```json { "data": { "meeting_session_id": "mtgsess_9b2f...", "provider": "recall", "scope": "provider_recording_media", "deletion_status": "requested" } } ``` `provider` identifies the media host (for example `recall`). `deletion_status` is `requested` or `already_in_progress`. Deletion removes the provider-hosted media only -- the session, its transcript, events, and artifacts remain intact. ## Related - [Join a Meeting](/docs/meeting/join-meeting) -- create a meeting session and send the bot - [Live Transcript](/docs/meeting/live-transcript) -- follow the transcript stream in real time - [Webhooks](/docs/meeting/webhooks) -- receive session events on your own endpoint --- ## Webhooks ### Webhooks > Source: https://developers.telnyx.com/docs/meeting/webhooks.md Instead of polling, point the bot at your own endpoint. Set `webhook_url` when creating the session and Telnyx pushes session events to it. The destination must be a public HTTPS URL without embedded credentials and must not resolve to loopback, private, link-local, or otherwise reserved addresses; redirects are not followed. Each session returns a `webhook_secret` exactly once at creation -- store it; it is not retrievable again. ## Envelope and Events Every delivery is an HTTP `POST` with `Content-Type: application/json`, no authorization header, and one envelope shape -- deduplicate on `id`: ```json { "id": "whdel_9f2c...", "event": "session.status_changed", "version": "1", "occurred_at": "2026-06-16T09:00:05Z", "data": {} } ``` | Event | `data` fields | | --- | --- | | `session.status_changed` | `session_id`, `status`, `status_detail`, `recording` | | `transcript.completed` | `session_id`, `segment_count`, `last_seq`, `ended_at` | | `recording.available` | `session_id`, `recording_types` (never URLs) | | `artifact.completed` | `session_id`, `artifact_id`, `type`, `content: {text}`, `model_provenance` | | `artifact.failed` | `session_id`, `artifact_id`, `type` | Each event's full payload schema lives in the **Webhooks** group of the API Reference. Individual `transcript.segment` messages are never delivered over webhooks. Read segments via the [transcript endpoint](/docs/meeting/live-transcript) or the stream. ## Verify Signatures Each delivery is signed with an `X-Meeting-Bot-Signature` header: ``` X-Meeting-Bot-Signature: t=1784297100,v1=5e8f...a3c1 ``` Compute HMAC-SHA256 over the timestamp's ASCII bytes, a literal `.` byte, and the exact raw request-body bytes, then hex-encode the result and compare it against `v1`. Do not parse and reserialize the JSON before verifying. ```python import hashlib import hmac header = "t=1784297100,v1=5e8f...a3c1" raw_body = request.body # exact bytes as received t = header.split(",")[0].split("=")[1] v1 = header.split(",")[1].split("=")[1] expected = hmac.new( webhook_secret.encode(), t.encode() + b"." + raw_body, hashlib.sha256, ).hexdigest() verified = hmac.compare_digest(v1, expected) ``` Concatenate the timestamp and body as bytes. Formatting `raw_body` into a string (for example with an f-string) produces `b'...'` in the signed payload and the comparison always fails. Use a constant-time comparison and reject deliveries whose timestamp is outside your accepted clock skew window. ## Delivery and Retry - Up to 5 delivery attempts. Non-2xx responses, redirects, network errors, and timeouts all consume an attempt. - Retries back off: roughly a minute after the first failed attempt, doubling each retry (about `60s * 2^n`). - Each attempt has a 10-second timeout, so respond with a 2xx promptly. - Events are delivered with best effort and duplicates can occur -- including when your endpoint processed an event but its response was lost. Deduplicate on the event `id` and fall back to REST polling when guaranteed receipt matters. ## Related - [Session Events](/docs/meeting/events) -- the full event history, and what to read when a delivery is missed - [Join a Meeting](/docs/meeting/join-meeting) -- set `webhook_url` when creating a session - [Collect Results](/docs/meeting/collect-results) -- read the finalized transcript and artifacts --- ## API Reference (Meeting (Beta)) ### Meeting Sessions - [Create a meeting session](https://developers.telnyx.com/api-reference/meeting-sessions/create-a-meeting-session.md): Creates a new meeting session. When an idempotency_key is supplied in the request body, replay lookup is scoped to the authenticated account and compares only… - [List meeting sessions](https://developers.telnyx.com/api-reference/meeting-sessions/list-meeting-sessions.md): Returns a list of meeting sessions, optionally filtered by status. - [Retrieve a meeting session](https://developers.telnyx.com/api-reference/meeting-sessions/retrieve-a-meeting-session.md): Retrieves a single meeting session by ID. A session that does not exist or that belongs to a different account both return 404. - [Update a meeting session](https://developers.telnyx.com/api-reference/meeting-sessions/update-a-meeting-session.md): Updates mutable properties of a meeting session. Only sessions in the scheduled state can be updated; any other state returns 409 with the invalid_state error… - [Delete a meeting session](https://developers.telnyx.com/api-reference/meeting-sessions/delete-a-meeting-session.md): Stops a meeting session without deleting its persisted record. Scheduled bots are cancelled, while bots that are joining or active are asked to leave. The pers… ### Meeting Session Actions - [Speak in a meeting session](https://developers.telnyx.com/api-reference/meeting-session-actions/speak-in-a-meeting-session.md): Sends audio / text-to-speech into a meeting session. - [Stop speaking in a meeting session](https://developers.telnyx.com/api-reference/meeting-session-actions/stop-speaking-in-a-meeting-session.md): Stops any active text-to-speech playback in a meeting session. - [Send chat in a meeting session](https://developers.telnyx.com/api-reference/meeting-session-actions/send-chat-in-a-meeting-session.md): Sends a chat message into a meeting session. ### Meeting Session Data - [List meeting session events](https://developers.telnyx.com/api-reference/meeting-session-data/list-meeting-session-events.md): Returns stored events ordered by ascending `seq`. To continue, pass the last returned item's `seq` as `after`. An empty page means no later stored events exist… - [List meeting session transcript](https://developers.telnyx.com/api-reference/meeting-session-data/list-meeting-session-transcript.md): Returns transcript segments ordered by ascending `seq`. Default `limit` is 100 and maximum is 1,000. Continue with `after=meta.next_after`. A long-poll timeout… - [List meeting session recordings](https://developers.telnyx.com/api-reference/meeting-session-data/list-meeting-session-recordings.md): Returns recordings for a meeting session. - [Delete meeting session recording media](https://developers.telnyx.com/api-reference/meeting-session-data/delete-meeting-session-recording-media.md): **Not yet available in production** — this route is not currently routed on api.telnyx.com and returns a generic 404; it is documented ahead of rollout. Irreve… ### Meeting Session Artifacts - [Create a meeting session artifact](https://developers.telnyx.com/api-reference/meeting-session-artifacts/create-a-meeting-session-artifact.md): Requests asynchronous generation of one `summary` or `action_items` artifact. Each type requires its own request. Generation requires transcript content and co… - [List meeting session artifacts](https://developers.telnyx.com/api-reference/meeting-session-artifacts/list-meeting-session-artifacts.md): Returns a list of artifacts for a meeting session. - [Retrieve a meeting session artifact](https://developers.telnyx.com/api-reference/meeting-session-artifacts/retrieve-a-meeting-session-artifact.md): Retrieves a single meeting session artifact by ID. ### Meeting Session Webhooks - [session.status_changed](https://developers.telnyx.com/api-reference/meeting-session-webhooks/session-status_changed.md): Sent when the session moves to a new lifecycle status. Deliveries are best effort and not guaranteed: up to 5 attempts with backoff that starts around 60 secon… - [transcript.completed](https://developers.telnyx.com/api-reference/meeting-session-webhooks/transcript-completed.md): Sent when the meeting transcript is finalized and ready to read. Individual `transcript.segment` messages are never delivered over webhooks; read segments via… - [recording.available](https://developers.telnyx.com/api-reference/meeting-session-webhooks/recording-available.md): Sent when a recording is ready to download. The payload carries only `recording_types`, never URLs; fetch short-lived download URLs from `GET /meeting_sessions… - [artifact.completed](https://developers.telnyx.com/api-reference/meeting-session-webhooks/artifact-completed.md): Sent when a summary or action-items artifact finishes generating. Deliveries are best effort and not guaranteed: up to 5 attempts with backoff that starts arou… - [artifact.failed](https://developers.telnyx.com/api-reference/meeting-session-webhooks/artifact-failed.md): Sent when an artifact generation fails. Deliveries are best effort and not guaranteed: up to 5 attempts with backoff that starts around 60 seconds and roughly…