# MeetStream AI | Documentation # Welcome to MeetStream! [**MeetStream.ai**](http://MeetStream.ai) provides a unified API to deploy interactive bots and AI agents into meeting platforms such as Zoom, Google Meet and Microsoft Teams. Use one integration to  * capture media * generate transcriptions * interact in real-time ## Meeting Bot and Agent Features ### Core Capabilities * Universal Integration: Connect to any major meeting platform via a single API using only the meeting URL. * Low-Latency Media: Capture raw audio and video with \~ 200ms latency. * Active AI Participants: Deploy bots that speak via TTS, share screens, or inject video streams. * Managed Infrastructure: Scale from one to thousands of concurrent sessions without managing bot clusters. * Rich Metadata: Track participant join/leave times, speaking duration, and in-meeting chat. - **Record:** Export mixed audio, isolated participant tracks, or composite MP4s. - **Stream:** Access real-time WebSocket audio (base64) and RTMP video feeds. * **Live Events**: Receive webhooks for real-time speaker attribution. * **Post-Meeting**: Access full, speaker-labeled transcripts via the MeetStream engine or third-party providers. - **Messaging:** Read and write in-meeting chat via API. - **A/V Output:** Stream live web pages, display static images, or speak using TTS. * **Automation:** Sync calendars for auto-dispatch and trigger auto-leave rules. * **Control:** Pause, resume, or mute bots mid-meeting via API. * **Resilience:** Built-in network auto-retries and platform consent handling. ## Use Cases * Bot joins live calls, streams real-time attributed transcripts via WebSocket * NLP layer detects intent signals and objections from the live transcript stream * Streams live attributed transcript via WebSocket, enabling real-time coaching cues to be surfaced to the rep mid-call * Post-call agent auto-generates follow-up summaries and action items for rep review - Bot silently joins instant or scheduled meetings and transcribes with speaker diarization in real time - NLP pipeline extracts decisions, action items, and open questions as structured objects - Each action item is attributed to a speaker with source timestamp for auditability - Output is pushed to tools like Notion, Jira, or Confluence via API post-meeting * Bot captures and transcribes internal meetings across Zoom, Meet, and Teams via one API * On meeting end, a webhook fires the full attributed transcript to your NLP pipeline for summarisation * Structured summary — decisions, action items, open questions — is generated from the transcript and pushed to Slack, Notion, or email * Raw transcript and diarization artifact are stored and available via API for any downstream processing - Bot captures the full call transcript with speaker attribution via MeetStream's API - Post-call, NLP layer extracts deal stage, contact details, and key notes from the transcript - Extracted fields are mapped and pushed to CRM objects via REST API — no manual entry - Works with any CRM that exposes a standard API; field mapping is configurable per org # Meeting Platforms Supported [MeetStream.ai](http://MeetStream.ai) supports seamless integration with the industry's leading video conferencing tools. Most platforms work out-of-the-box, while others require a one-time configuration to enable bot access, which we have provided step-by-step guides for in our Get Started section. | Platform | Supported | Setup Requirement | Guide Link | | --------------- | --------- | ----------------- | ----------------------------------------------------------------------------------------------------- | | Google Meet | Yes | None | - | | Zoom | Yes | Yes | [Zoom Bot Setup Guide](https://docs.meetstream.ai/guides/app-integrations/zoom-marketplace-app-setup) | | Microsoft Teams | Yes | None | - | More exciting platform integrations, including **Webex** and **Slack** are coming soon, stay tuned for updates. # Getting Started > Step-by-step guides to help you integrate and use MeetStream effectively ## Meeting Bot API Setup To start using MeetStream AI, set up your account, obtain API credentials, and prepare your environment: Sign up at our [dashboard](https://app.meetstream.ai/api-key) Open the API tab and create an API key Use this API key in the `Authorization` header for all API calls. **API Base URL**: `https://api.meetstream.ai/api/v1/` hosts all bot control and data retrieval endpoints. **Regions**: MeetStream currently uses a unified endpoint with built-in regional compliance; region-specific endpoints may arrive later. * **Organization & Workspace**: Manage organizations, workspaces, and team members in the dashboard. Confirm your API key belongs to the workspace where you plan to create bots. * **Configure Webhook Endpoints**: For asynchronous events (status updates, transcripts, etc.), add webhook URLs in the dashboard’s Webhooks section. Use publicly accessible HTTPS endpoints and store any signing secret for verification. * **SDKs (Optional)**: Call the REST API directly or install any available client libraries. Include the API key in the `Authorization` header (`Token YOUR_API_KEY`) for every request. * **Test in Development**: Create test meetings and send bots before going live. Tools like `ngrok` help expose local servers so you can verify webhook handling and connectivity. Once your credentials, webhooks, and tooling are in place, you’re ready to create bots and dive into platform-specific configuration and advanced MeetStream features. ## Bot Integration Guides Learn how to integrate MeetStream with Zoom meetings in two simple steps: * **[Zoom App Setup](https://docs.meetstream.ai/guides/app-integrations/zoom-marketplace-app-setup)** - Create and configure your Zoom app in the marketplace and connect with MeetStream to create your first zoom bot. No integration required. follow the [create-bot](https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/create-bot) endpoint to create a bot. No integration required. follow the [create-bot](https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/create-bot) endpoint to create a bot. ## Advanced Features Set up live transcription with webhook integration Automate bot scheduling with calendar events Build automated workflows with meeting data ## Quick Links **Need help?** Check out our [API documentation](https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/create-bot) or contact support for assistance. * [Create your first bot](/api-reference/api-endpoints/bot-endpoints/create-bot) * [Monitor bot status](/api-reference/api-endpoints/bot-endpoints/get-bot-status) * [Get meeting transcripts](/api-reference/api-endpoints/transcription/get-transcription) * [Calendar integration](/api-reference/api-endpoints/calendar/create-calendar) ## Community Resources Join our community to stay updated and get help: * 📖 [Documentation](https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/create-bot) * 🚀 [Dashboard](https://dashboard.meetstream.ai) *** # MeetStream Quickstart: Create Your First Bot This guide helps new MeetStream users create their first bot, join a meeting, and retrieve recordings. --- > Note: [Dashboard Setup](/guides/get-started/dashboard-setup) must be completed before proceeding with the below steps. ## 1) Create a bot (join Zoom / Google Meet / Microsoft Teams) Use the **Create Bot** endpoint: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/create-bot ### What you need to send - `meeting_link`: paste the meeting link (Zoom / Google Meet / Microsoft Teams). - `video_required`: set `true` if you want video, otherwise `false`. > Note: Currently MeetStream supports **Google Meet, Zoom, and Microsoft Teams**. ### Example cURL ```bash curl -X POST "https://api.meetstream.ai/api/v1/bots/create_bot" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "meeting_link": "", "video_required": false }' ``` Once successful, you’ll receive a response containing details like the `bot_id`. Save it — you’ll use it to fetch audio/video or remove the bot. --- ## 2) Retrieve audio and video after the bot leaves After the call ends (or the bot leaves), you can fetch recordings: ### Get bot audio https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/get-bot-audio ### Get bot video https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/get-bot-video > Depending on your setup, processing may take a short time after the bot exits. --- ## 3) Remove/stop the bot from an active meeting You can remove the bot using: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/remove-bot ### Example cURL ```bash curl -X POST "https://api.meetstream.ai/api/v1/bots/bot_id/remove_bot" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "bot_id": "" }' ``` --- ## Troubleshooting tips - Double-check whether you’re using the correct **API key** and header format: - `Authorization: Token ` - Ensure the `meeting_link` is a valid Zoom/GMeet/Teams meeting URL. - If video isn’t needed, keep `video_required=false` to reduce bandwidth and processing time. --- If you need more details, see the full docs: https://docs.meetstream.ai/api-reference # Idempotency Key Guide for Bot Creation This guide explains how to use the `Idempotency-Key` header on the **Create Bot** endpoint to safely retry requests without creating duplicate bots. > **Scope:** This feature is currently supported **only on `POST /api/v1/bots/create_bot`** (bot creation). Other write endpoints (PATCH scheduled bots, remove bot, etc.) do not honor `Idempotency-Key` yet. --- ## 1) Why idempotency matters When you call the Create Bot API, several real-world conditions can cause your client to retry the same request: - The network drops mid-request and your HTTP client retries. - API Gateway returns a 5xx and your retry library kicks in. - Your worker queue redelivers the same job because the previous worker didn't ack in time. - A user double-clicks the "Join meeting" button in your UI. Without idempotency, every one of these retries would create a **new bot** — and you'd end up with two, three, or more bots in the same meeting, each consuming credits. `Idempotency-Key` solves this: MeetStream remembers the key you sent, ties it to the bot that got created, and replays the original bot's details on every subsequent retry — never creating a duplicate. --- ## 2) How it works (one-paragraph summary) You generate a unique string per logical bot creation (a UUID is ideal) and pass it in the `Idempotency-Key` HTTP header. The first request with that key creates a bot normally and returns `HTTP 201`. Every subsequent request that sends the **same key from the same API key/user** returns `HTTP 507` with the original bot's details — the request body is ignored, no new bot is created, no credits are charged again. Keys are scoped per user (the API key owner). The same key value sent by two different MeetStream accounts will **not** collide. --- ## 3) Quick example ### First call — creates the bot ```bash curl -X POST "https://api.meetstream.ai/api/v1/bots/create_bot" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 9b1c3f4a-7e2b-4d8f-9a1c-2e6f4b8a0c11" \ -d '{ "meeting_link": "https://meet.google.com/abc-defg-hij", "video_required": false }' ``` Response (`HTTP 201`): ```json { "bot_id": "bot_a1b2c3d4...", "transcript_id": "t_111...", "meeting_url": "https://meet.google.com/abc-defg-hij", "status": "Active" } ``` ### Retry with the same key — replays the original ```bash curl -X POST "https://api.meetstream.ai/api/v1/bots/create_bot" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 9b1c3f4a-7e2b-4d8f-9a1c-2e6f4b8a0c11" \ -d '{ "meeting_link": "https://meet.google.com/abc-defg-hij", "video_required": false }' ``` Response (`HTTP 507`): ```json { "bot_id": "bot_a1b2c3d4...", "transcript_id": null, "meeting_url": "https://meet.google.com/abc-defg-hij", "status": "Active" } ``` The bot ID and meeting URL match the original. No new bot was created. No credits were charged. The `status` field reflects the bot's status **at the time of the replay** — so if the bot has progressed from `Joining` to `Active` between calls, you'll see the latest state. --- ## 4) The header | Header | Required | Format | Description | |---|---|---|---| | `Idempotency-Key` | No | Any non-empty string (UUID v4 strongly recommended) | Unique per logical bot-creation request. | Header rules: - **Case-insensitive.** Both `Idempotency-Key` and `idempotency-key` are accepted. (REST API Gateway preserves casing, HTTP API lowercases — MeetStream handles both.) - **Whitespace is trimmed.** Leading/trailing whitespace is removed before lookup. - **Whitespace-only values are ignored** (treated as if the header was absent). - **No length cap enforced** — but keep keys reasonably short. UUID v4 (`9b1c3f4a-7e2b-4d8f-9a1c-2e6f4b8a0c11`) is the canonical choice. If you omit the header, the endpoint behaves exactly as before — every call creates a new bot. --- ## 5) Response codes | Status | When | Meaning | |---|---|---| | `201 Created` | First call for a `(user, key)` pair, or any call without the header | Bot was created. Standard create-bot response body. | | `507` | Subsequent call with a key that was already bound to a bot | Replay of the original bot. **Treat this as a success.** No new bot was created. | | `400 / 403 / 429 / 500` | Validation, wallet, or platform errors | Standard error responses. The key is **not** cached when the request fails before a bot row is written — your retry will execute fresh. | ### 507 replay response shape ```json { "bot_id": "", "transcript_id": null, "meeting_url": "", "status": "" } ``` > **Why `transcript_id` is `null` on replay:** the transcript ID is generated and returned only at original creation time. If you need it later, fetch it from the bot detail endpoint using the `bot_id`. --- ## 6) Rules and guarantees ### What MeetStream guarantees 1. **No duplicate bots from sequential retries.** Once a `(user_id, idempotency_key)` pair is bound to a bot, every future request with that pair replays the original bot — forever. 2. **Per-user isolation.** The same key value used by two different MeetStream accounts will **not** collide. Keys are scoped to your API key's user. 3. **Request body is ignored on replay.** If you change the `meeting_link`, `bot_name`, or any other field but send the same key, you still get the original bot back. To create a different bot, use a different key. 4. **Failed creates are not cached.** If the original request returned a 4xx/5xx **before** any bot record was written (validation error, wallet rejection, server error), a retry with the same key will be treated as a fresh attempt. ### What MeetStream does **not** guarantee 1. **No in-flight (409) detection.** If you fire two requests with the same key in parallel **before the first one finishes writing to the database**, both may create a bot. Subsequent retries will then deterministically resolve to whichever bot the index surfaces first. - **Mitigation:** in your client code, serialize retries (don't fire them in parallel). For UUID-keyed clients this is almost never an issue. 2. **No key expiration.** Keys are bound for the lifetime of the bot record. **Never reuse a key for a different logical create** — treat each new bot creation as a fresh UUID. 3. **Lookup fails open.** If MeetStream's internal lookup encounters a transient database error, the request proceeds to create a new bot rather than reject the call. In that very rare case you may see one duplicate bot. This is intentional: we prefer a rare duplicate over refusing legitimate traffic. --- ## 7) Best practices ### DO - **Generate a fresh UUID v4 per logical create.** Use your language's standard UUID library — `uuid.uuid4()` (Python), `crypto.randomUUID()` (Node), `UUID.randomUUID()` (Java), etc. - **Persist the key alongside your job/queue record** before the HTTP call, so a retry uses the same key. - **Treat `507` as success.** Parse the body and proceed as if you'd just created the bot. - **Generate the key on the client that owns the retry loop** — typically your worker or the entry point of your background job. The key must survive across retry attempts. - **Use UUIDs**, not auto-incrementing integers or timestamps. UUIDs eliminate the risk of accidental collisions when scaling out workers. ### DON'T - **Don't reuse a key across different logical creates.** If a user clicks "Join meeting" twice for two different meetings, generate two different keys. If you reuse the same key, the second click will return the first bot's details (wrong meeting!). - **Don't fire parallel retries with the same key.** Serialize your retry loop. - **Don't generate the key inside the retry attempt.** A new UUID per attempt defeats the purpose. Generate it once, outside the retry loop. - **Don't rely on the key as a unique identifier in your own system.** Use the `bot_id` returned by MeetStream for tracking and lookups. --- ## 8) Reference implementations ### Python (with `requests` and `tenacity`) ```python import uuid import requests from tenacity import retry, stop_after_attempt, wait_exponential API_URL = "https://api.meetstream.ai/api/v1/bots/create_bot" API_KEY = "" def create_bot(meeting_link: str, video_required: bool = False) -> dict: idempotency_key = str(uuid.uuid4()) headers = { "Authorization": f"Token {API_KEY}", "Content-Type": "application/json", "Idempotency-Key": idempotency_key, } payload = { "meeting_link": meeting_link, "video_required": video_required, } return _send_with_retries(headers, payload) @retry(stop=stop_after_attempt(5), wait=wait_exponential(min=1, max=30)) def _send_with_retries(headers: dict, payload: dict) -> dict: response = requests.post(API_URL, json=payload, headers=headers, timeout=10) if response.status_code in (201, 507): return response.json() if response.status_code >= 500: response.raise_for_status() raise ValueError(f"Bot creation failed: {response.status_code} {response.text}") ``` Key points: - `idempotency_key` is generated **once**, outside the retry decorator, so all retries share the same key. - Both `201` and `507` are treated as success. - Only `5xx` triggers a retry; `4xx` errors fail fast (no point retrying a validation error). ### Node.js (with `axios` and `axios-retry`) ```javascript import axios from "axios"; import axiosRetry from "axios-retry"; import { randomUUID } from "crypto"; const API_URL = "https://api.meetstream.ai/api/v1/bots/create_bot"; const API_KEY = ""; const client = axios.create({ timeout: 10_000 }); axiosRetry(client, { retries: 5, retryDelay: axiosRetry.exponentialDelay, retryCondition: (err) => err.response?.status >= 500 || !err.response, }); export async function createBot(meetingLink, videoRequired = false) { const idempotencyKey = randomUUID(); const response = await client.post( API_URL, { meeting_link: meetingLink, video_required: videoRequired }, { headers: { Authorization: `Token ${API_KEY}`, "Content-Type": "application/json", "Idempotency-Key": idempotencyKey, }, validateStatus: (status) => status === 201 || status === 507, } ); return response.data; } ``` ### Worker queue pattern (recommended) If you're creating bots in response to a queue message (SQS, Kafka, BullMQ, Sidekiq, etc.), generate and **persist** the idempotency key when the message is first written, not when the worker picks it up: ```python def enqueue_bot_creation(meeting_link: str, user_event_id: str): job = { "meeting_link": meeting_link, "user_event_id": user_event_id, "idempotency_key": str(uuid.uuid4()), } sqs.send_message(QueueUrl=QUEUE_URL, MessageBody=json.dumps(job)) def worker_handler(message: dict): job = json.loads(message["Body"]) response = requests.post( API_URL, json={"meeting_link": job["meeting_link"]}, headers={ "Authorization": f"Token {API_KEY}", "Idempotency-Key": job["idempotency_key"], }, ) assert response.status_code in (201, 507) ``` If the worker crashes after the HTTP call but before acking the message, the queue redelivers the same job — but the same key replays the original bot. No duplicate. --- ## 9) FAQ **Q: Does the idempotency key work for scheduled bots (`join_at` in the future)?** A: Yes. `Idempotency-Key` is honored on every call to `POST /api/v1/bots/create_bot`, including scheduled-bot creations. A retry returns the same scheduled bot's details. **Q: What happens if I send `Idempotency-Key` but no `Authorization` header?** A: The request is rejected at the auth layer before idempotency is evaluated. No key is bound. **Q: Can I look up a bot by its idempotency key later?** A: Not via a dedicated endpoint. The intended use is retry safety, not as a public lookup key. Use the `bot_id` returned in the response to fetch bot details. **Q: Are keys deleted when a bot is deleted?** A: The key lives on the bot record. If the bot record is deleted, the key is gone with it and the same key could be reused. **Best practice is still to never reuse keys** — always generate a fresh UUID. **Q: Does idempotency-key affect billing?** A: A `507` replay does **not** charge credits — only the original `201` creation did. Failed creates (4xx/5xx) also don't charge. **Q: What if my client sends the header with a value of `null` or an empty string?** A: That's treated as if the header was absent. Every call creates a new bot. **Q: Is there a maximum number of bots I can bind to one key?** A: One. A key maps to exactly one bot. After that, every future request with that key replays that one bot. --- ## 10) Related docs - [Create Bot Payload Reference](https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/create-bot) — full list of body fields accepted by `POST /api/v1/bots/create_bot`. - [First Bot Quickstart](https://docs.meetstream.ai/guides/get-started/create-your-first-bot) — end-to-end walkthrough of creating your first bot. - [Bot Lifecycle Webhook Events](https://docs.meetstream.ai/guides/webhooks/webhooks-and-events) — webhook events you'll receive after the bot is created. - API reference: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/create-bot # Guide to Configure Various Automatic Leave Triggers for the Bot This guide explains how to make your MeetStream bot **automatically leave a meeting when only other vendor notetakers remain** — Otter, Fireflies, tl;dv, Read.ai, Fathom, Grain, Copilot, Notta, and so on. It's an opt-in nested mechanism inside the existing `automatic_leave` payload field on `POST /api/v1/bots/create_bot` (and honored identically when you `PATCH` a scheduled bot). > **Scope:** Works on all three supported platforms — Google Meet, Zoom, and Microsoft Teams. Same payload shape everywhere. [Section 9](#9-the-other-automatic_leave-timeouts) also covers the rest of the `automatic_leave` knobs — `waiting_room_timeout`, `noone_joined_timeout`, `everyone_left_timeout`, `voice_inactivity_timeout`, `in_call_recording_timeout`, and `recording_permission_denied_timeout` — since `bot_detection` interacts with all of them. --- ## 1) Why this exists `automatic_leave.everyone_left_timeout` only fires when the participant count drops to **zero**. In practice, a single lingering vendor notetaker (Otter, Fireflies, tl;dv, …) keeps the participant count at one and pins your bot to the meeting: - The bot keeps recording empty audio. - You keep accruing recording minutes. - The transcript fills with crosstalk between notetakers, or with silence. - A Fargate task stays warm until `in_call_recording_timeout` (default 4 hours) finally pulls the plug. Turning on `bot_detection` lets the bot recognise that **every remaining participant looks like another notetaker**, wait a short countdown to be sure, and then leave cleanly. --- ## 2) How it works (one paragraph) You give the bot a list of bot-name keywords (e.g. `["otter", "fireflies", "tl;dv"]`). After the bot has been admitted into the meeting for `activate_after` seconds, it begins checking the roster on every change. Whenever every remaining non-self participant's display name contains **at least one** of those keywords, the bot arms a `timeout`-second countdown. If a human reappears before the countdown fires, the timer is cancelled. If the condition still holds when the countdown fires, the bot leaves the meeting and you get the usual `bot.leaving` → `bot.stopped` webhook sequence. The bot **never matches itself** against the keyword list — it identifies its own row in the roster via a stable platform identity (Google Meet `deviceId` / Zoom `user_id` / Teams `bot_id`), not its display name. Naming your bot `MeetStream Notetaker` will not cause a false positive even if `notetaker` is in `matches`. --- ## 3) Quick example ```bash curl -X POST "https://api.meetstream.ai/api/v1/bots/create_bot" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "meeting_link": "https://meet.google.com/abc-defg-hij", "bot_name": "MeetStream", "automatic_leave": { "everyone_left_timeout": 600, "bot_detection": { "using_participant_names": { "matches": [ "notetaker", "recorder", "assistant", "copilot", "otter", "fireflies", "tl;dv", "read.ai", "fathom", "grain", "fellow", "notta", "krisp" ], "activate_after": 300, "timeout": 30 } } } }' ``` What that payload says, in plain English: > _"Five minutes after I'm admitted to the meeting, start watching the roster. The moment every other participant looks like a notetaker, give them 30 seconds to prove otherwise — and if they don't, leave."_ --- ## 4) Field reference All `bot_detection` fields live under `automatic_leave.bot_detection` on the create-bot payload. ### `automatic_leave.bot_detection` | Field | Type | Required | Description | |---|---|---|---| | `using_participant_names` | `object` | Yes (when `bot_detection` is present) | Detect bots by matching their display name against a keyword list. See below. | > Other mechanisms from Recall.ai's spec — `using_participant_events` (no-speaker / no-screen-share heuristic) and `silence_detection` (audio-energy fallback) — are **not yet supported**. Passing them returns `HTTP 400` so we don't silently ignore them. ### `automatic_leave.bot_detection.using_participant_names` | Field | Type | Required | Default | Range | Description | |---|---|---|---|---|---| | `matches` | `array of string` | **Yes** | — | non-empty list | Case-insensitive substrings. A participant is treated as a bot when their display name **contains** any of these substrings. Stored lowercased and deduped. | | `activate_after` | `int` (seconds) | No | `300` | `60`–`1800` | Delay before the check becomes active, counted from when the bot is admitted to the meeting. Lets real participants trickle in before the check arms. | | `timeout` | `int` (seconds) | No | `5` | `5`–`300` | Once every remaining participant matches, how long to wait before actually leaving. The timer resets if a human reappears. | ### Validation rules - `matches` must be a non-empty array of non-empty strings. Whitespace is trimmed, casing is normalised to lowercase, duplicates are dropped — so `["Otter", "otter", " OTTER "]` becomes `["otter"]` server-side. - `activate_after` and `timeout` are strict integers (no booleans, no numeric strings). Out-of-range values return `HTTP 400`. - The block is **opt-in**: omit `bot_detection` (or omit `using_participant_names`) to disable. The rest of `automatic_leave` continues to apply. - Validation runs in **two paths**: `POST /api/v1/bots/create_bot` and `PATCH /api/v1/calendar/scheduled_bots/{bot_id}`. They share the same schema, so anything that's valid at create time is valid at patch time. --- ## 5) Behaviour timeline Imagine a meeting that opens at `t=0` and your bot is admitted at `t=2s` with `activate_after=300, timeout=30`: | Time | Roster | What the bot does | |---|---|---| | `t=0–2s` | (joining) | No check yet. | | `t=2s` | Bot admitted, `_in_meeting_since` clock starts. | Check is **dormant** until `t=302s`. | | `t=10s` | Bot + 2 humans + 1 Otter notetaker. | Dormant. | | `t=180s` | Both humans leave; only bot + Otter remain. | Still dormant — `activate_after` hasn't elapsed. | | `t=302s` | Bot + Otter still in meeting. | Condition holds → arms a 30s timer. | | `t=320s` | Late human joins. | Condition broken → **timer cancelled**. | | `t=400s` | That human leaves again; only bot + Otter remain. | Condition holds again → re-arms a fresh 30s timer. | | `t=430s` | Still bot + Otter. | Timer fires → bot leaves the meeting. | What you see externally: - A `bot.leaving` webhook event followed by `bot.stopped` (terminal). The `message` field on the underlying status update will mention `bot_detection.using_participant_names` so you can attribute the exit. - No `bot.kicked` / `bot.denied` / `bot.failed` — this is a graceful, customer-configured exit, not a platform-side eject. - Recording, transcription, and the rest of the post-call pipeline run as normal. You'll still receive `audio.processed`, `transcription.processed`, `video.processed`, and `bot.done` after the artifacts are ready. > See [Bot Lifecycle Webhook Events](https://docs.meetstream.ai/guides/webhooks/webhooks-and-events) for the full webhook sequence. --- ## 6) Choosing `matches` The keywords below are a good starting baseline that catches most common notetakers in the wild. Substring matches, case-insensitive: ```json [ "notetaker", "recorder", "assistant", "copilot", "otter", "fireflies", "tl;dv", "read.ai", "fathom", "grain", "fellow", "notta", "krisp", "meetgeek", "supernormal", "tactiq", "rewatch", "circleback" ] ``` Tips: - **Match the visible display name, not a brand.** Most notetakers join with a name like `"Otter.ai Notetaker"`, `"tl;dv Bot"`, `"Fathom AI Notetaker"`, etc. — picking up either the brand fragment (`otter`) or the role fragment (`notetaker`) is usually enough. - **Substring, not exact.** `"otter"` matches `"Otter.ai Notetaker"`, `"OtterPilot"`, and `"otter-bot"` — you don't need separate entries. - **No regexes.** This is plain `lowercase(name).contains(keyword)` per keyword. Special characters (`.`, `:`, `;`) are matched literally. - **Don't include your own bot name.** It's safe (the bot excludes itself by stable identity) but it's noise. --- ## 7) Tuning `activate_after` and `timeout` | Profile | `activate_after` | `timeout` | When to use | |---|---|---|---| | **Aggressive** | `60`–`120` | `5`–`30` | Internal meetings where you know the call kicks off quickly. Faster reaction, slightly higher false-positive risk if a human joins late. | | **Balanced** *(recommended)* | `300` | `30`–`60` | Default. Gives stragglers five minutes to arrive, then leaves within a minute of a roster going bot-only. | | **Conservative** | `600`–`1200` | `60`–`300` | Customer-facing meetings or webinars where it's important to avoid leaving early. Strongly biases toward staying. | Rule of thumb: **err on the side of larger values**. A false negative (bot stays when it should leave) is recoverable — the existing `everyone_left_timeout`, `in_call_recording_timeout`, and voice-inactivity fallbacks will eventually fire. A false positive (bot leaves while real people are still in the meeting) is not. --- ## 8) Self-identity: how the bot knows which row is itself Each platform exposes a different stable identity for the bot's own row in the roster. You don't have to do anything — this is handled automatically — but it's useful to understand because it explains a subtle edge case in Google Meet. | Platform | Self-identity used | Notes | |---|---|---| | **Google Meet** | `deviceId` (captured from the `(You)` marker in the Meet UI on join) | Captured non-blocking. If capture hasn't completed (rare), the check **fails closed** — it skips that tick and cancels any armed timer, then retries on the next tick. | | **Microsoft Teams** | `bot_id` (stable from the bot's `TeamsBotConfig`) | Available as soon as the websocket is connected. | | **Zoom** | `my_participant_id` returned by the Zoom SDK | Available as soon as the bot joins the participants list. | Because of this, **naming your own bot to match a `matches` keyword is safe across all three platforms** — the self row is excluded by ID, never by name. **Google Meet caveat:** in the rare case two of your own bots launch into the same meeting at almost the same instant *and* their self-identity capture races, the bot may briefly perceive its sibling as "another notetaker". The fail-closed safety means it will not leave on uncertain identity — but if you regularly send two bots to the same meeting, set `activate_after` ≥ 300s so the capture has fully settled by the time the check arms. --- ## 9) The other `automatic_leave` timeouts `bot_detection` runs **alongside** the existing timeouts on the `automatic_leave` object — it doesn't replace any of them. They each cover a different failure mode and they can all be configured in the same payload: ```json "automatic_leave": { "waiting_room_timeout": 600, "noone_joined_timeout": 600, "everyone_left_timeout": 300, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60, "bot_detection": { /* … */ } } ``` Field-by-field reference below. All values are integer **seconds**. ### `waiting_room_timeout` | | | |---|---| | **What it controls** | Max time the bot will sit in the waiting room / lobby before giving up and exiting. | | **Range** | `60` – platform max (GMeet: `600`, Zoom: `1200`, Teams: `1800`). Out-of-range returns `HTTP 400`. | | **Default** | `600` for GMeet/Zoom, `1200` for Teams. | | **Triggers when** | The host has not admitted the bot before this timeout elapses. | | **Terminal webhook** | `bot.notallowed` (the host never admitted the bot in time). | Use a smaller value for ad-hoc bots so you don't keep a container alive waiting for an admit that's never coming. Use a larger value for scheduled bots where the host may take a few minutes to start the meeting. ### `noone_joined_timeout` | | | |---|---| | **What it controls** | How long to stay in an empty meeting waiting for the first other participant to join. | | **Range** | `60` – `1800`. Out-of-range returns `HTTP 400`. | | **Default** | `600` for GMeet/Zoom, `1200` for Teams. | | **Triggers when** | The bot has been admitted, but the participant count *besides the bot* has been `0` for this many seconds since join. | | **Terminal webhook** | `bot.stopped` (graceful — nobody was coming). | This handles the "host opened the meeting but the participants never showed up" case. It's distinct from `everyone_left_timeout`, which only kicks in after a participant joined and *then* left. ### `everyone_left_timeout` | | | |---|---| | **What it controls** | How long to wait after the meeting goes empty (apart from the bot) before exiting. | | **Range** | `60` – `1800`. Out-of-range returns `HTTP 400`. | | **Default** | `600` for GMeet/Zoom, `1200` for Teams. | | **Triggers when** | At least one other participant joined at some point and the roster has since dropped to bot-only. | | **Terminal webhook** | `bot.stopped`. | If you're using `bot_detection` (above), this is your **safety net** — it still fires if all the notetakers leave too, leaving the bot truly alone. The two cover different shapes of "the meeting is over": `bot_detection` for "humans gone, notetakers stayed", `everyone_left_timeout` for "everyone gone, including the notetakers". ### `voice_inactivity_timeout` | | | |---|---| | **What it controls** | How long the bot tolerates total silence in a meeting where participants are present but nobody is speaking. | | **Range** | Container-enforced. Sensible practical range is `60` – `1800`. | | **Default** | `600` (10 minutes). | | **Triggers when** | The bot is admitted, the roster is non-empty, but no audio activity has been observed for this many consecutive seconds. | | **Terminal webhook** | `bot.stopped` with the message `Bot exited the call: Voice inactivity timeout`. | | **Notes** | Useful for catching meetings that "ended" without anyone formally leaving — e.g. attendees walked away from their desks but kept their tab open. Independent of `bot_detection`: voice inactivity fires regardless of who is in the roster. | ### `in_call_recording_timeout` | | | |---|---| | **What it controls** | Absolute hard cap on how long a single bot session can stay in the meeting once recording has started. | | **Range** | `600` – `18000` (10 minutes to 5 hours). Out-of-range returns `HTTP 400`. | | **Default** | `14400` (4 hours). | | **Triggers when** | The cumulative time since the bot started recording exceeds this value. | | **Terminal webhook** | `bot.stopped`. | Treat this as a **failsafe**, not a knob you tune meeting-by-meeting. It exists so a stuck recording can't run for days against your credits. Leave the default unless you have a specific reason (e.g. multi-hour all-hands) to extend it. ### `recording_permission_denied_timeout` *(Zoom only)* | | | |---|---| | **What it controls** | How long to wait for the host to act on Zoom's "allow recording" prompt before treating it as a denial and leaving. | | **Range** | `60` – `300`. Out-of-range returns `HTTP 400`. | | **Default** | `60`. | | **Triggers when** | The bot has joined a Zoom meeting and asked for recording permission, but the host has neither granted nor denied within this window. | | **Terminal webhook** | `bot.recording_permission_denied` → `bot.leaving` → `bot.stopped`. See the [Bot Lifecycle Webhook Events Guide](https://docs.meetstream.ai/guides/webhooks/webhooks-and-events). | | **Platforms** | Zoom only. The field is silently ignored on GMeet and Teams payloads. | Most Zoom hosts respond to the recording prompt within a few seconds; the default of `60` is a good balance. Raise it only if your hosts routinely take a minute or more to acknowledge prompts. --- ### Putting them together If multiple timeouts would fire at once, whichever expires first wins. In practice that means: | Scenario | What ends the meeting | |---|---| | Host never admits the bot | `waiting_room_timeout` → `bot.notallowed` | | Bot admitted, nobody else ever shows up | `noone_joined_timeout` → `bot.stopped` | | Real meeting happens, everyone (including notetakers) eventually leaves | `everyone_left_timeout` → `bot.stopped` | | Humans leave, vendor notetakers linger | `bot_detection` → `bot.stopped` (typically well before `everyone_left_timeout`) | | Roster non-empty but room is silent | `voice_inactivity_timeout` → `bot.stopped` | | Multi-hour meeting runs longer than expected | `in_call_recording_timeout` → `bot.stopped` (failsafe) | | *(Zoom only)* Host doesn't respond to the recording prompt | `recording_permission_denied_timeout` → `bot.recording_permission_denied` → `bot.stopped` | --- ## 10) Common pitfalls ### "My bot is leaving meetings too early." Most likely your `matches` list is too aggressive for your customer base — e.g. you have `"ai"` in there and a real participant named `"Ainsley Thompson"` is being treated as a bot. - Substrings match anywhere in the name. Prefer specific brand fragments (`"otter.ai"`, not `"ai"`). - Raise `activate_after` to give real people more time to arrive before the check arms. - Raise `timeout` so a brief network blip on the human's side doesn't cause an early exit. ### "My bot isn't leaving even though only notetakers remain." Three things to check, in order: 1. **Did you actually configure it?** The block is opt-in. Inspect the bot's stored config via `GET /api/v1/bots/{bot_id}` — the `automatic_leave` you see there is what the bot received. 2. **Are the notetakers' display names in the meeting actually matching?** Google Meet sometimes shows `"Unknown"` for the first ~10 seconds of a join before its name resolver populates a real name. While a participant is unnamed, they don't match — so the bot can't conclude "only bots remain". Wait for names to settle. 3. **Has `activate_after` elapsed?** The check is dormant during that window. If you set `activate_after: 1200` and the notetakers leave after only 600 seconds, the check never armed in the first place — `everyone_left_timeout` takes over instead. ### "How do I disable it on a scheduled bot?" Send a PATCH to `/api/v1/calendar/scheduled_bots/{bot_id}` with the `bot_detection` key set to `{}`, or with `automatic_leave` set to an `automatic_leave` object that doesn't contain the `bot_detection` block. The next time the bot is launched from that schedule, the feature will be disabled. --- ## 11) FAQ **Q: Is this on by default?** A: No. It's strictly opt-in. Bots created without an `automatic_leave.bot_detection` block behave exactly as before. **Q: Does it cost extra?** A: No. There's no separate charge — you simply stop being billed for the meeting time you would otherwise spend waiting for `everyone_left_timeout`. **Q: Can I change the keywords after the bot has joined?** A: No. The config is locked in at bot creation (or at the most recent PATCH for a scheduled bot before the bot launches). There is no live-update path. **Q: What happens if a human keeps their name as just an emoji or a single letter?** A: Their name won't match any keyword (substring match requires at least one keyword to appear in the name). They will be treated as a human → the bot stays. False-positive-safe. **Q: What if a bot in the meeting uses a non-Latin-script name?** A: Substring matching is locale-agnostic — but the keywords you provide must literally appear in the participant's `displayName` as the platform reports it. If a notetaker joins with a name in Cyrillic or CJK, add those substrings to `matches` too. **Q: Does it work for breakout rooms?** A: The bot does not currently follow participants into breakout rooms. `bot_detection` operates on the main-room roster only. **Q: Will the post-call transcript still be generated?** A: Yes. A `bot_detection` exit is treated as a normal graceful leave — the post-call pipeline runs in full (audio upload, transcription, video processing, manifest). **Q: Is the leave event distinguishable from a normal `bot.leaving`?** A: The webhook event name is the same (`bot.leaving` → `bot.stopped`), but the underlying status update message includes the substring `bot_detection.using_participant_names`. If you need to attribute exits, parse the `message` field on the bot's status history. --- ## 12) Related docs - [Create Bot Payload Reference](https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/create-bot) — full payload schema, including the `automatic_leave.bot_detection` block. - [Bot Lifecycle Webhook Events](https://docs.meetstream.ai/guides/webhooks/webhooks-and-events) — the `bot.leaving` → `bot.stopped` sequence you'll observe when this fires. - [Calendar Integration Guide](https://docs.meetstream.ai/guides/calendar-integrations/google-calendar-o-auth-setup) — applying `bot_detection` to scheduled bots via PATCH. # MeetStream Guide: Custom S3 Storage By default, MeetStream stores all bot media — audio, video, transcripts, screenshots, chat logs, and meeting manifests — in its own S3 bucket, and serves them to you via presigned URLs when you call the fetch endpoints. **Custom S3 Storage** lets you direct MeetStream to upload all of that media directly into **your own S3 bucket** using credentials you provide. Use cases: - Compliance or data-residency requirements (your data never leaves your AWS account). - Direct integration with your own storage pipeline or data lake. - Full control over file lifecycle, retention, and access policies. --- ## 1) Prerequisites 1. An AWS S3 bucket in the region of your choice. 2. An IAM user (or role) with credentials that grant at least **write access** (`s3:PutObject`) to that bucket. If you also want MeetStream to serve the files back to you via its API, you'll need to additionally grant **read access** (`s3:GetObject`). 3. A MeetStream API key. ### Minimum IAM policy Replace `your-bucket-name` with your bucket name. ```json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:PutObjectAcl" ], "Resource": "arn:aws:s3:::your-bucket-name/meetstream/*" } ] } ``` If you also want the MeetStream API to serve presigned download URLs from your bucket (i.e. `access_mode: "read_write"`), add `s3:GetObject` to the same statement. > If you set a custom `prefix` (see below), scope the `Resource` to that prefix instead — e.g. `arn:aws:s3:::your-bucket-name/recordings/meetstream/*`. If you use per-category `prefixes`, add one `Resource` entry per distinct prefix (e.g. `.../recordings/audio/*`, `.../recordings/video/*`, …). --- ## 2) Configure your S3 bucket ```http PUT /api/v1/admin/configs?config_type=storage Authorization: Token Content-Type: application/json ``` ```json { "provider": "aws", "bucket_name": "your-bucket-name", "region": "us-east-1", "access_key_id": "AKIA...", "secret_key": "...", "access_mode": "read_write", "prefix": "recordings/meetstream", "prefixes": { "audio": "recordings/audio", "video": "recordings/video", "transcript": "transcripts", "metadata": "metadata" } } ``` ### Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `provider` | string | Yes | Storage provider. Only `"aws"` is supported. | | `bucket_name` | string | Yes | Name of your S3 bucket. | | `region` | string | Yes | AWS region of the bucket (e.g. `"us-east-1"`). | | `access_key_id` | string | Yes | AWS access key ID for an IAM user with access to the bucket. | | `secret_key` | string | Yes | AWS secret access key. | | `access_mode` | string | No | `"read_write"` (default) or `"write_only"`. See [Access Modes](#3-access-modes). | | `prefix` | string | No | Base key prefix under which media is stored. Defaults to `"meetstream"`. Acts as the fallback for any artifact category not given its own prefix in `prefixes`. Leading/trailing slashes are stripped and `..` segments are rejected. | | `prefixes` | object | No | Per-artifact-category prefix overrides. Optional keys: `audio`, `video`, `transcript`, `metadata`. Each lets you store that category under its own top-level path (e.g. audio under `recordings/audio/{bot_id}/...`). Any category omitted falls back to `prefix`. Same normalization and `..` rejection as `prefix`. See [File layout](#4-file-layout-in-your-bucket). | | `endpoint_url` | string | No | Custom S3-compatible endpoint (e.g. for MinIO or Cloudflare R2). Omit for standard AWS S3. | ### Validation For `read_write`, MeetStream performs a live `HeadBucket` check against your bucket before saving the configuration. If the credentials are invalid or the bucket doesn't exist, the request will fail with a `400` error. For `write_only`, MeetStream validates by writing a tiny probe object under **each distinct prefix** you configured (`{prefix}/.write_probe_*`, plus one per unique category prefix). This confirms your credentials can write without requiring read access, and works even if your IAM policy is scoped to those individual prefixes. If any probe write fails, the request returns a `400` error. ### Response ```json { "message": "Storage config saved successfully", "provider": "aws", "bucket_name": "your-bucket-name", "region": "us-east-1", "access_mode": "read_write", "prefix": "recordings/meetstream", "prefixes": { "audio": "recordings/audio", "video": "recordings/video", "transcript": "transcripts", "metadata": "metadata" } } ``` > Your `access_key_id` and `secret_key` are stored securely and are **never returned** in any API response. --- ## 3) Access modes | Mode | MeetStream uploads to your bucket | MeetStream API serves files from your bucket | |------|----------------------------------|---------------------------------------------| | `read_write` (default) | Yes | Yes — fetch endpoints return presigned URLs backed by your bucket | | `write_only` | Yes | No — fetch endpoints return `403` | Use `write_only` when: - Your bucket is private and you want to control access yourself (generate your own presigned URLs). - Your IAM policy only grants write permissions. - You are piping files into your own processing pipeline and don't need MeetStream to serve them. > If you start with `write_only` and later upgrade to `read_write`, you can immediately use the fetch endpoints — no re-upload needed. --- ## 4) File layout in your bucket MeetStream does **not** create a per-bot subfolder. Files are written directly into your configured prefix folder, with the **bot id as a filename prefix** (`{prefix}/{bot_id}_`). This keeps each bot's objects grouped by name while letting many bots share one folder. With the default prefix: ``` meetstream/ abc123_audio.wav abc123_meeting_recording.mp4 abc123_participants.json abc123_chats.json abc123_manifest.json abc123_screenshots/ 9f2b….png 9f2b….json a1c3….png a1c3….json def456_audio.wav ← a second bot, same folder def456_manifest.json ``` Or, with `"prefix": "recordings/meetstream"`, the same files live under `recordings/meetstream/abc123_...`. > Multi-file collections (`screenshots/`, and transcript JSON under `transcription///`) keep a `{bot_id}_/` subfolder so their many files stay grouped — there is still no bare `{bot_id}/` folder. ### Per-category prefixes Each file belongs to one of four **artifact categories**, and you can route each category to its own top-level prefix via the `prefixes` object. Any category you don't set inherits the base `prefix`. | Category | Files | Configured by | |----------|-------|---------------| | `audio` | `audio.wav`, `audio.mp3` | `prefixes.audio` | | `video` | `meeting_recording.mp4` | `prefixes.video` | | `transcript` | `transcription/.../raw_transcript.json`, `processed_transcript.json` | `prefixes.transcript` | | `metadata` | `manifest.json`, `participants.json`, `chats.json`, `screenshots/` | `prefixes.metadata` | For example, with `"prefixes": {"audio": "recordings/audio", "video": "recordings/video", "transcript": "transcripts", "metadata": "metadata"}`: ``` recordings/audio/abc123_audio.wav recordings/video/abc123_meeting_recording.mp4 transcripts/abc123_transcription/deepgram//processed_transcript.json metadata/abc123_manifest.json metadata/abc123_screenshots/9f2b….png ``` Prefixes isolate MeetStream files from any other objects in your bucket. You can change any prefix at any time; media written before a change remains fetchable at its original location (MeetStream records the exact bucket and key prefix used for each bot). New bots use the updated prefixes. --- ## 5) Fetching media after configuration Once custom storage is configured, all subsequent bots write their media to your bucket. The MeetStream fetch endpoints work exactly the same as before — you do not need to change how you call them. ### Fetch endpoints and URL behaviour | Endpoint | Behaviour with `read_write` | Behaviour with `write_only` | |----------|-----------------------------|-----------------------------| | `GET /bots/{bot_id}/get_audio` | Presigned URL from your bucket (1-hour expiry) | `403` | | `GET /bots/{bot_id}/get_video` | Presigned URL from your bucket (1-hour expiry) | `403` | | `GET /bots/{bot_id}/screenshots` | Paginated list of screenshots with presigned URLs (1-hour expiry). **Not supported for Zoom bots.** | `403` | | `GET /bots/{bot_id}/screenshots/{id}` | Single screenshot by UUID with presigned URL (1-hour expiry). **Not supported for Zoom bots.** | `403` | | `GET /bots/{bot_id}?type=chat` | JSON content read from your bucket | `403` | | `GET /bots/{bot_id}?type=participants` | JSON content read from your bucket | `403` | | `GET /bots/{bot_id}?type=transcription` | JSON content read from your bucket | `403` | | `GET /transcript/{transcript_id}/get_transcript` | JSON content read from your bucket | `403` | The `403` response for `write_only` includes a message indicating where the file lives: ```json { "message": "Media is stored in your S3 bucket, but only write access was granted. Update your storage configuration to read+write to fetch media via the API.", "access_mode": "write_only" } ``` --- ## 6) Existing bots are not affected by configuration changes Each bot's media is pinned to whichever bucket was active when its files were written (after the bot leaves the meeting). Enabling, disabling, or changing custom storage will not affect media from bots that have already been processed — those files remain in the bucket they were originally written to. | Scenario | Where the files are | |----------|---------------------| | Bot processed before custom storage was enabled | Platform default bucket | | Bot processed after custom storage was enabled | Your custom bucket | | Custom storage disabled after a bot is processed | Files stay in your custom bucket | --- ## 7) View your current storage configuration ```http GET /api/v1/admin/configs Authorization: Token ``` Returns your current storage configuration. Credential fields are never included in the response. ```json { "StorageConfig": { "aws": { "active": true, "bucket_name": "your-bucket-name", "region": "us-east-1", "access_mode": "read_write", "prefix": "recordings/meetstream", "prefixes": { "audio": "recordings/audio", "video": "recordings/video", "transcript": "transcripts", "metadata": "metadata" }, "configured_at": "2025-01-15T10:00:00Z", "last_validated_at": "2025-01-15T10:00:00Z" } } } ``` --- ## 8) Delete your storage configuration ```http DELETE /api/v1/admin/configs?key_name=aws Authorization: Token ``` This removes your credentials and storage configuration. After deletion, new bots will have their media written to the MeetStream platform bucket. Existing bots whose files were already written to your bucket are not affected — those files are not deleted from your bucket. --- ## 9) Troubleshooting **Configuration save fails with `400`** - Confirm the bucket name and region are correct. - Confirm the IAM credentials have `s3:PutObject` and `s3:GetObject` (for `read_write`) on the bucket. - Confirm `access_mode` is `"read_write"` if you want live validation. **Fetch endpoint returns `403` with `"access_mode": "write_only"`** - Your storage is configured as `write_only`. Either access the file directly from your bucket, or update your configuration to `read_write`. **Fetch endpoint returns `404` for a bot that was processed** - The file may not have been written to your bucket due to a permissions error during upload. Check that your IAM credentials are valid and have `s3:PutObject` on `arn:aws:s3:::your-bucket-name/meetstream/*` (or your configured prefix, e.g. `arn:aws:s3:::your-bucket-name/recordings/meetstream/*`). **Presigned URL returns `403 Forbidden`** - The URL may have expired — presigned URLs have a short lifetime (see the table in [Section 5](#5-fetching-media-after-configuration)). Call the fetch endpoint again to get fresh URLs. - Your IAM policy may not include `s3:GetObject`. Update the policy and your storage configuration. **Bot was processed before I enabled custom storage — files are not in my bucket** - This is expected. Only bots processed after the configuration was saved write to your bucket. See [Section 6](#6-existing-bots-are-not-affected-by-configuration-changes). # Zoom Bot Implementation Guide > Step-by-step guide to set up your Zoom app integration with MeetStream ## Step 1: Set up your Zoom app in the Zoom App Marketplace Follow the steps below to create and configure your Zoom app. Sign in or sign up to access the Zoom App Marketplace. Go to the Zoom Marketplace **Recommendation** Use a dedicated/service account to avoid ownership issues later. 1. Go to the **Develop** tab on the top right of the page 2. Click the dropdown and select **Build App** 3. Select **General App** Zoom App Marketplace Zoom App Marketplace Select how permissions will be granted: Individual users authorize access to their Zoom account. Admins authorize access for the entire organization. Choose app type 1. Go to the **Basic Information** tab 2. Scroll to **OAuth Information** 3. Paste the below **Callback URL** into the OAuth Redirect URL field ``` https://api.meetstream.ai/api/v1/admin/zoom/oauth/callback ``` Callback url paste 4. Scroll further down in the same page and find **OAuth Allow Lists** and paste the below URL there ``` https://meetstream.ai ``` allow lists url paste 1. Under **Features** tab click on **Embed**, and enable **Meeting SDK** The rest can be ignored for now, just following what is mentioned is enough to connect our platform to zoom. Enable Recording SDK 1. Scroll to the top of the menu on the left or go to **Basic Information** 2. Find **App Credentials** 3. Copy your **Client ID** and **Client Secret** copy credentials ## Step 2: Connect Zoom with MeetStream **Prerequisite:** Make sure your Zoom app is created and configured by following the above steps. 1. Open the [MeetStream Dashboard](https://app.meetstream.ai/integrations) 2. Go to the **Integrations** tab 3. Navigate to the **Zoom** section 4. Paste your **Client ID** and **Client Secret** MeetStream Zoom credentials Complete the OAuth authorization flow to connect your Zoom account. authorise connection Once authorized, your integration is ready. MeetStream Zoom credentials Use the Create Bot API to start a bot. View full API reference **Endpoint:** `POST /bots/create_bot` ```bash cURL curl -X POST "https://api.meestream.ai/api/v1/bots/create_bot" \ -H "Authorization: Token YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "meeting_link": "", "bot_name": "", "bot_message": "", "live_audio_required": { "websocket_url": "" }, "live_transcription_required": { "webhook_url": "" }, "custom_attributes": { "tag": "", "sample": "" }, "callback_url": "" }' ``` ```json Request Body { "meeting_link": "", "bot_name": "", "bot_message": "", "live_audio_required": { "websocket_url": "" }, "live_transcription_required": { "webhook_url": "" }, "custom_attributes": { "tag": "", "sample": "" }, "callback_url": "" } ``` **Responses:** - **201:** Bot created successfully - **400:** Invalid request - **401:** Unauthorized - **500:** Server error If recording permission is denied, consider exiting early to avoid missing recordings. 1. Go to the **Bots** section in your dashboard 2. Find your bot 3. Click **View** to monitor and debug Bot Explorer The above steps enable working for meetings hosted by your account only, to join meetings hosted by any account follow our [Zoom App Production Submission Guide](https://docs.meetstream.ai/guides/app-integrations/zoom-app-production-submission) ## Troubleshooting - Invalid or expired meeting link - App is in development mode - Waiting room not approved - Password-protected meeting - Audio not enabled in request - No active speakers - Permissions not granted by host - Processing delay after meeting - Invalid API key - Incorrect header format (`Token YOUR_API_KEY`) - Missing permissions - Rate limits exceeded # Zoom Production App Submission In the [Zoom Marketplace App Setup](https://docs.meetstream.ai/guides/app-integrations/zoom-marketplace-app-setup) guide, you created your Zoom app, added your development credentials to MeetStream, and got your first bot running. However, in development mode your bot can **only join meetings hosted by your own Zoom account**. This guide walks you through submitting your Zoom app for production approval, so your bots can join meetings hosted by anyone. --- ## Step 1: Verify Your Development Tab Settings Before switching to the Production tab, make sure the following are correctly set in your app's **Development** tab. Open your app at [marketplace.zoom.us](https://marketplace.zoom.us/). ### Basic Information → OAuth Information - **OAuth Redirect URL** must be set to `https://meetstream.ai` - Leave **Use Strict Mode** and **Subdomain Check** unchecked ### Features Go through each item in the Features section of the sidebar: - **Access** — No changes needed - **Surface** — No changes needed - **Embed** — Enable **Meeting SDK**. Leave **Device OAuth** off embed tab changes - **Connect** — Leave untouched ### Scopes The `zak` scope should already be present from your initial setup. No additional scopes are needed for core bot functionality. > **If you are using Zoom Calendar features**, you may need additional scopes such as `user:read` or `meeting:list`. You must also provide a **Scope Description** — a written justification for why your app needs the scopes it uses. Enter the following: > *"We are using this scope to send our bot to the meeting and capture the meeting data. The data is stored encrypted in our own S3 bucket — primarily audio data, as a WAV file."* ### Actions No configuration required. --- ## Step 2: Configure the Production Tab Once your Development tab is in order, click the **Production** tab at the top of your app page. You will now fill out the Production-specific configuration. ### Embed Set this identically to your Development tab: - Enable **Meeting SDK** - Leave **Device OAuth** off ### Scopes Add the same scopes as in Development (`zak`, plus any extras you configured). Use the same scope description as above. ### App Listing This is the public-facing information Zoom reviewers will read. Fill it in with **your own company's details**. | Field | What to enter | |---|---| | **App Name** | Your product or app name | | **Company Name** | Your registered company name | | **Short Description** | A one-line summary of what your app does | | **Terms of Use URL** | Link to your terms of service | | **Support URL** | Link to your support page or contact | | **Documentation URL** | Link to your developer documentation | **Long Description** — Describe what your app does and how it uses Zoom. Be clear and specific as this is reviewed by Zoom. Cover what data you capture, how it is stored, and what developers use it for. Example: > *[Your app] uses the Zoom Meeting SDK to deploy bots that join meetings, capture audio, and [describe your use case — e.g. transcribe, analyse, summarise]. Captured data is stored securely in [your infrastructure].* ### Monetization Not required. Skip this section. ### Technical Design This section has two tabs: **Overview** and **Security**. #### Overview — Technology Stack Describe how your app is built and how it integrates with Zoom. Cover the Zoom APIs you use, the scopes and why, and how data is handled. Example: > *Our app uses the Zoom Meeting SDK to deploy automated bots that join meetings. It uses OAuth 2.0 for user authentication and the Zoom REST API to retrieve and manage meeting data.* > > **Zoom APIs used:** > - OAuth 2.0 — user authentication and secure token access > - Scopes: `meeting:read`, `user:read`, `recording:read`, `meeting:write` > > **Zoom REST API:** > - Meetings API — to retrieve, create, or update meeting data > - Users API — to retrieve user details linked to accounts > - Recordings API — to access cloud recordings (if enabled) > > **Zoom Webhooks:** > - Subscribed to meeting start/end and participant join/leave events #### Overview — Architecture Diagram Upload a diagram showing how your system connects to Zoom and your users. It should illustrate: - Your users/developers interacting with your app - Your app connecting to Zoom via the **Meeting SDK** - The bot joining a Zoom meeting - Data flowing back to your storage infrastructure Use any tool (Lucidchart, draw.io, Excalidraw) and export as PNG or JPG. #### Security Answer both security questions: 1. **Do you have a secure software development process (SSDLC)?** Select **Yes** and upload a document describing your SDLC process. 2. **Does your application undergo SAST and/or DAST testing?** Select **Yes** and upload supporting evidence (e.g. scan reports). MeetStream Dashboard Zoom credentials ### Additional Documents Upload any extra supporting materials — such as a short screen recording showing your bot joining a Zoom meeting and capturing data. --- ## Step 3: Submit for Review 1. Click **App Submission** in the left sidebar. 2. Check the submission checklist. Every item must show as complete — any section marked **Not ready** must be addressed first. 3. Click **Submit** to send your app to Zoom for review. Zoom's review typically takes a few business days. You'll receive an email when your app is approved. --- ## Step 4: Swap in Your Production Credentials Once Zoom approves your app: 1. Go back to your app in the [Zoom App Marketplace](https://marketplace.zoom.us/). 2. Navigate to **App Credentials → Production**. 3. Copy your **Production Client ID** and **Production Client Secret**. 4. Open the [MeetStream Dashboard](https://app.meetstream.ai/) and go to **Zoom SDK Credentials** in the sidebar. 5. Replace your development credentials with the production ones and save. Your MeetStream bots will now be able to join Zoom meetings hosted by any user — not just your own account. For multi-tenant OAuth based Zoom authentication (OBF), check out our [Zoom OBF Implementation Guide](https://docs.meetstream.ai/guides/app-integrations/zoom-obf-implementation) --- ## Troubleshooting | Issue | What to check | |---|---| | App Submission shows "Not ready" | Every section under the Production tab must be fully completed before submission unlocks. | | Scope description rejected by Zoom | Be specific — state what data is captured, how it is stored, and exactly why the scope is needed. | | Architecture diagram won't upload | Export as PNG or JPG and keep the file under 5MB. | | Production credentials not visible after approval | Refresh the App Credentials page and check your approval email for any additional steps required. | | Bots still failing after swapping credentials | Confirm the production credentials were saved in the MeetStream dashboard with no extra spaces or characters. | # Zoom OBF implementation in MeetStream This guide walks you through connecting your end-users' Zoom accounts to MeetStream so bots can join their Zoom meetings using MeetStream's hosted **OBF** (OAuth-based) flow. Each end-user authorizes the same Zoom Marketplace app you registered, and MeetStream stores **one connection per end-user**. At create-bot time you tell MeetStream which end-user the bot should act on behalf of by passing that end-user's Zoom user id. **Production API base URL:** `https://api.meetstream.ai` (use your environment's host if different). --- ## What you need - A Zoom account with permission to create apps in the [Zoom App Marketplace](https://marketplace.zoom.us/). - The [MeetStream User Dashboard](https://app.meetstream.ai) to manage Zoom OAuth connections (the dashboard obtains the Zoom authorize URL and sends the user's browser there). - A **deployed HTTPS server** you operate at your **redirect URL** (for example `https://yourapp.example.com/zoom/oauth/callback`). After Zoom redirects an end-user's browser to this URL with an authorization `code`, **this server** must call MeetStream's connection-create endpoint (see Step 2). MeetStream does not complete OAuth in the browser at your callback — **your backend** must call the connection-create endpoint. A minimal Node example lives in this repo at **`examples/zoom-meetstream-oauth-callback`**. - The same **MeetStream user API key** (`Authorization: Token …`) for **authorize-url** (used when starting each end-user's flow) and **connection-create** (used on your server). The **same redirect URI string** everywhere: Zoom app, authorize-url, and connection-create. --- ## Step 1: Create a Zoom app and set the redirect URL 1. Go to the [Zoom App Marketplace](https://marketplace.zoom.us/) and sign in. 2. Create a single OAuth app for your product. **You only register ONE app, even if you have many end-users.** Each end-user authorizes that same app under their own Zoom account. 3. In your Zoom app's OAuth settings, add **your** redirect URL (for example `https://yourapp.example.com/zoom/oauth/callback`). The same string must appear in: - Zoom's **OAuth Redirect URL** / allow list fields (per Zoom's UI), and - The `redirect_uri` query parameter when MeetStream's **authorize-url** API is called, and - The JSON `redirect_uri` field on **`POST https://api.meetstream.ai/api/v1/zoom/oauth/connections`**. All three must match **byte-for-byte** (scheme `https`, host, path, no unintended trailing slash). 4. Required scopes: `user:read:user`, `user:read:token` (so MeetStream can identify each authorizing end-user and mint OBF tokens on their behalf). 5. App distribution: depends on your scenario. - If your end-users are inside **a single Zoom account** (e.g. an internal tool), an account-level / internal app is fine. - If your end-users are spread across **many Zoom accounts** (typical B2B SaaS), publish your app on the Zoom Marketplace so it can be installed by any Zoom account. For development, use the **same browser session** for editing the Zoom app and for completing consent if Zoom or your tunnel (e.g. ngrok) relies on cookies. --- ## Step 2: One OAuth flow per end-user (creates a Zoom OAuth Connection) You will run this flow **once per end-user**. Each completed flow stores a separate connection on MeetStream, keyed by the end-user's Zoom user id. 1. In your product's UI, when an end-user (e.g. Alice) clicks **Connect Zoom**: - Your backend calls **`GET https://api.meetstream.ai/api/v1/zoom/oauth/authorize-url?redirect_uri=&state=`** with `Authorization: Token `. - The optional `state` query parameter is round-tripped to Zoom and back to your callback URL — use it to carry your own end-user identifier (e.g. `alice-internal-uuid`) through the OAuth flow. - Open the returned `authorize_url` in the end-user's browser. 2. The end-user signs in to **their own** Zoom account and approves the app. Zoom redirects their browser to **your** redirect URI with `?code=&state=`. 3. **Your server** receives the request and calls MeetStream: **`POST https://api.meetstream.ai/api/v1/zoom/oauth/connections`** - **Header:** `Authorization: Token ` - **Body (JSON):** ```json { "code": "", "redirect_uri": "", "metadata": { "tenant_user_id": "" } } ``` - **Response (200):** ```json { "zoom_user_id": "", "zoom_account_id": "", "email": "", "display_name": "", "metadata": { "tenant_user_id": "" }, "state": "connected", "created_at": "...", "updated_at": "...", "has_refresh_token": true } ``` **Save `zoom_user_id` against the end-user record in your own DB.** You will pass it back when creating bots for this end-user. 4. Repeat steps 1–3 for every additional end-user (Bob, Carol, …). Each flow uses **the same** Zoom Marketplace app (same `client_id` / `client_secret`); only the authorizing end-user changes. If anything fails, confirm the three `redirect_uri` strings match Zoom's registered URL and that the same **user API key** is used for authorize-url and connection-create. ### Listing, fetching, and deleting connections | Method | Path | Purpose | | -------- | ------------------------------------------------------- | -------------------------------------------------- | | `GET` | `/api/v1/zoom/oauth/connections` | List all Zoom OAuth connections for your account | | `GET` | `/api/v1/zoom/oauth/connections/{zoom_user_id}` | Get a single connection (no token material) | | `DELETE` | `/api/v1/zoom/oauth/connections/{zoom_user_id}` | Disconnect that Zoom user | All endpoints use `Authorization: Token `. Token material (`access_token` / `refresh_token`) is never returned by these endpoints; only identity, metadata, state, and timestamps. --- ## Step 3: Create a bot that joins as a specific end-user (`create_bot`) When you create a bot for Alice's meeting, pass Alice's Zoom user id (the one returned by the connections endpoint when Alice authorized) under `zoom`: ```json { "meeting_link": "https://zoom.us/j/123456789?pwd=...", "bot_name": "MeetStream Bot", "audio_required": true, "video_required": false, "zoom": { "use_zoom_obf": true, "zoom_oauth_connection_user_id": "" } } ``` - `meeting_link` must be a **Zoom** meeting URL. - `use_zoom_obf` must be a boolean (`true` or `false`). - `zoom_oauth_connection_user_id` is **required** when `use_zoom_obf=true`. It must match the `zoom_user_id` returned by `POST /api/v1/zoom/oauth/connections` for the end-user this bot should act on behalf of. Do **not** send custom token server fields such as `zoom.obf_url` or `zoom.zak_url`; MeetStream supplies those when hosted Zoom auth is enabled. --- ## Troubleshooting | Issue | What to try | |--------|-------------| | `zoom.zoom_oauth_connection_user_id is required when zoom.use_zoom_obf=true` | Add the field to your create_bot payload using the value returned from `POST /api/v1/zoom/oauth/connections`. | | `No Zoom users connected for this account` | The end-user has not completed the OAuth flow. Send them through `GET /api/v1/zoom/oauth/authorize-url` and complete the connection. | | `Zoom user is not connected for this account` | The `zoom_user_id` you passed does not match any stored connection under your MeetStream account. Verify the value and that you're using the right MeetStream user API key. | | `Zoom user is disconnected (no valid refresh token)` | Zoom revoked the refresh token (password change, app uninstalled, idle 90+ days). Have that end-user reconnect via the OAuth flow. | | Redirect or "invalid redirect" from Zoom | The URL Zoom uses must exactly match the Zoom app, authorize-url `redirect_uri`, and connection-create `redirect_uri`. | | Error that `use_zoom_obf` is invalid | Ensure it is a JSON boolean, not a string. | | Bot created but meeting is not Zoom | `use_zoom_obf` is only for Zoom links; use a Zoom `meeting_link`. | --- ## Summary 1. **Zoom Marketplace:** Register one HTTPS callback URL (e.g. `…/zoom/oauth/callback`) for your single Zoom OAuth app. 2. **Per end-user OAuth:** `authorize-url` (with `state=`) → end-user authorizes → your callback calls `POST /api/v1/zoom/oauth/connections` → save the returned `zoom_user_id` against your end-user record. 3. **`create_bot`:** Add `"zoom": { "use_zoom_obf": true, "zoom_oauth_connection_user_id": "" }` for Zoom meetings. # Google Signed-In Bots > This guide applies to **Google Meet**. By default, MeetStream bots join as anonymous users. A signed-in bot logs into a real Google account before joining — so it appears as a named participant, not a guest. **Use this when:** - The meeting blocks anonymous/guest users - You want the bot to appear with a name and avatar instead of "Unknown" ---
--- ## Prerequisites - A **Google Workspace account** with a custom domain (e.g. `yourcompany.com`) - Access to your **Google Workspace Admin Console** - Terminal access to run an OpenSSL command --- ## Setup Navigate to [admin.google.com](https://admin.google.com). workspace admin Go to **Security → Authentication → SSO with third-party IdPs**. tab navigation Select **SSO with third-party IdPs**, then open **Legacy SSO profile**. legacy sso profile Turn on **Enable legacy SSO profile** and fill in these URLs: Sign-in page URL ``` https://api.meetstream.ai/api/v1/bot/gmeet-sign-in ``` Sign-out page URL ``` https://api.meetstream.ai/api/v1/bot/gmeet-sign-out ``` Also enable **Domain-specific issuer** on the same page. legacy sso profile legacy sso profile Run this command in your terminal: ```bash openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -sha256 -days 3650 -nodes ``` This creates two files: - `key.pem` — your private key - `cert.pem` — your certificate Save both. You'll need them in the next steps. In the [MeetStream dashboard](https://app.meetstream.ai/integrations), go to **Integrations → Google Signed-In Bots**. dashboard Enter the custom domain you use with your Google Workspace account. add domain Add each email address you use to invite the bot. For every email you add, upload the `key.pem` and `cert.pem` certificate generated in Step 5. > Upload the certificate each time you add a new mail ID under a domain. adding user --- ## Deploy Your Signed-In Bot Now you are ready to use Google meet using configured email identity! When calling the [Create Bot](/api-reference/api-endpoints/bot-endpoints/create-bot) endpoint, pass the following parameter to activate Google Meet sign-in: ```json "google_meet": { "login_required": true, "google_login_domain": "your domain name" } ``` Here is an example with a full working payload that supports signed-in bots for google meet: ```json { "meeting_link": "{{meeting_link}}", "bot_name": "{{bot_name}} Agent", "video_required": {{video_required}}, "bot_message": "Hey Everyone, I'm a speaking agent", "bot_image_url":"{{bot_profile}}", "google_meet": { "login_required": true, "google_login_domain": "salesrobin.ai", "sign_in_email": "bot.user@acme.com", "strict_email": true }, "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "{{user_id}}" }, "recording_config": { "retention": { "type": "timed", "hours": 48 } }, "automatic_leave": { "waiting_room_timeout": 100, // sec "everyone_left_timeout": 100, // secs "voice_inactivity_timeout": 51, // secs "in_call_recording_timeout": 14400, // secs "recording_permission_denied_timeout": 60 // only for zoom } } ``` --- For general bot setup, see the [Create Your First Bot](/guides/get-started/create-your-first-bot) guide and webhook event handling, see [Webhooks and Events](/guides/webhooks/webhooks-and-events). # MeetStream Guide: Calendar Integration & Auto-Scheduling This guide explains how to connect your Google Calendar to MeetStream so bots can automatically join your meetings — no manual API calls required. Applies to: **Google Meet, Zoom, Microsoft Teams** meetings on your Google Calendar. --- ## What you get with Calendar Integration Once connected, MeetStream can: - **See your upcoming meetings** — synced directly from Google Calendar with incremental sync for speed - **Schedule bots for specific meetings** — one click or one API call - **Auto-schedule bots for all meetings** — hands-free, every day - **Handle recurring meetings** — automatically reschedule bots for the next occurrence - **React to calendar changes in real time** — if a meeting is rescheduled, the bot's join time updates automatically; if a meeting is cancelled, the bot is cancelled too - **Manage scheduled bots** — list, update, or delete scheduled bots at any time - **Keep webhooks alive** — watch channels are automatically renewed before they expire --- ## 1) Get your Google OAuth credentials Before connecting your calendar, you need three things from Google: a **Client ID**, **Client Secret**, and **Refresh Token**. Follow these steps to get them. ### Step 1: Create a Google Cloud project and OAuth credentials 1. Go to the [Google Cloud Console](https://console.cloud.google.com/). 2. Create a new project (or use an existing one). 3. Navigate to **APIs & Services > Library** and enable the **Google Calendar API**. 4. Go to **APIs & Services > Credentials**. 5. Click **Create Credentials > OAuth 2.0 Client ID**. 6. Set the application type to **Web application**. 7. Under **Authorized redirect URIs**, add: ``` http://localhost:3000/api/google/oauth-callback ``` 8. Click **Create** and copy your **Client ID** and **Client Secret**. ### Step 2: Run the OAuth helper to get your refresh token MeetStream provides a lightweight Node.js helper that runs the OAuth consent flow locally and returns your refresh token. **Prerequisites:** Node.js installed on your machine. 1. Create a `.env` file in the project root with your credentials: ``` GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= ``` 2. Install dependencies ```bash npm install express googleapis dotenv ``` 3. Create the server.js file and paste this code ```typescript const express = require('express'); const { google } = require('googleapis'); require('dotenv').config(); const app = express(); const PORT = 3000; const REDIRECT_URI = `http://localhost:${PORT}/api/google/oauth-callback`; const oauth2Client = new google.auth.OAuth2( process.env.GOOGLE_CLIENT_ID, process.env.GOOGLE_CLIENT_SECRET, REDIRECT_URI ); const scopes = [ 'https://www.googleapis.com/auth/calendar.events.readonly', 'https://www.googleapis.com/auth/calendar.readonly', 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile', ]; app.get('/', (_req, res) => { const authUrl = oauth2Client.generateAuthUrl({ access_type: 'offline', scope: scopes, prompt: 'consent', include_granted_scopes: true, }); res.redirect(authUrl); }); app.get('/api/google/oauth-callback', async (req, res) => { const { code, error } = req.query; if (error) { return res.status(400).send(`

OAuth Error

${error}

`); } if (!code) { return res.status(400).send('

Missing authorization code

'); } try { const { tokens } = await oauth2Client.getToken(code); res.send(`

Success!

Refresh Token

${tokens.refresh_token || 'Not returned — revoke access at https://myaccount.google.com/permissions and try again'}

Access Token

${tokens.access_token}

Expiry

${new Date(tokens.expiry_date).toISOString()}


Copy the refresh token into your .env file, then stop this server.

`); console.log('\n=== TOKENS ==='); console.log('Refresh Token:', tokens.refresh_token); console.log('Access Token:', tokens.access_token); console.log('Expiry:', new Date(tokens.expiry_date).toISOString()); } catch (err) { res.status(500).send(`

Token Exchange Failed

${err.message}
`); } }); app.listen(PORT, () => { console.log(`Open http://localhost:${PORT} to start the OAuth flow`); console.log(`Redirect URI: ${REDIRECT_URI}`); console.log('\nMake sure this redirect URI is added in Google Cloud Console → Credentials → OAuth 2.0 Client → Authorized redirect URIs'); }); ``` 4. Start the helper server ```bash node server.js ``` You'll see: ``` Open http://localhost:3000 to start the OAuth flow Redirect URI: http://localhost:3000/api/google/oauth-callback Make sure this redirect URI is added in Google Cloud Console → Credentials → OAuth 2.0 Client → Authorized redirect URIs ``` 3. Open **http://localhost:3000** in your browser. 4. Sign in with your Google account and grant calendar access. 5. After authorization, the page displays your **Refresh Token** and **Access Token**. 6. Copy the **Refresh Token** — you'll need it in the next step. > If the refresh token is not returned, revoke MeetStream's access at [https://myaccount.google.com/permissions](https://myaccount.google.com/permissions) and try again. Google only returns a refresh token on the first consent or after a revoke. ### Required scopes The helper requests these scopes (read-only calendar access + basic profile): | Scope | Purpose | |---|---| | `calendar.events.readonly` | Read calendar events | | `calendar.readonly` | Read calendar list | | `userinfo.email` | Identify the Google account | | `userinfo.profile` | Basic profile info | You now have everything you need: **Client ID**, **Client Secret**, and **Refresh Token**. --- ## 2) Connect your Google Calendar With your credentials ready, connect your Google Calendar to MeetStream. ### API endpoint ``` POST https://api.meetstream.ai/api/v1/calendar/create_calendar ``` ### Request body ```json { "google_client_id": "", "google_client_secret": "", "google_refresh_token": "" } ``` ### What happens behind the scenes 1. MeetStream validates your credentials by refreshing the access token with Google. 2. Fetches all your calendars (primary, secondary, shared). 3. Stores your credentials securely (encrypted at rest in AWS SSM Parameter Store). 4. Sets up **push notification channels** on all calendars you have write access to — so MeetStream is notified in real time when events are created, updated, or cancelled. 5. Fetches your Google profile info (email, name) for display purposes. ### Example cURL ```bash curl -X POST "https://api.meetstream.ai/api/v1/calendar/create_calendar" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "google_client_id": "", "google_client_secret": "", "google_refresh_token": "" }' ``` ### Response ```json { "calendar_id": "google_calendar_usr_abc123", "platform": "google_calendar", "user_email": "user@example.com", "user_name": "Jane Smith", "calendars": [ { "id": "user@example.com", "summary": "My Calendar", "isPrimary": true, "accessRole": "owner", "selected": true }, { "id": "team-meetings@group.calendar.google.com", "summary": "Team Meetings", "isPrimary": false, "accessRole": "writer", "selected": true } ], "primary_calendar_id": "user@example.com", "watch_setup": { "success": true, "watches_setup": 2, "watch_results": [ { "calendar_id": "user@example.com", "calendar_summary": "My Calendar", "channel_id": "a1b2c3d4-...", "expiration": "1748000000000" } ], "failed_calendars": [] }, "message": "Calendar connected successfully" } ``` > **Note:** If you call this endpoint again with updated credentials, MeetStream replaces the existing connection — no need to disconnect first. --- ## 3) View your calendars After connecting, you can list all calendars linked to your Google account. ### API endpoint ``` GET https://api.meetstream.ai/api/v1/calendar/calendars ``` ### Example cURL ```bash curl -X GET "https://api.meetstream.ai/api/v1/calendar/calendars" \ -H "Authorization: Token " ``` ### Response ```json { "calendars": [ { "id": "user@example.com", "summary": "My Calendar", "isPrimary": true, "accessRole": "owner", "timeZone": "America/New_York", "backgroundColor": "#4285f4", "selected": true } ], "total": 1, "user_id": "usr_abc123" } ``` > This is a live call to the Google Calendar API and always returns the latest calendar list. --- ## 4) Sync and view your events MeetStream syncs events from Google Calendar and stores them locally. It detects meeting links for supported platforms (Google Meet, Zoom, Microsoft Teams, Webex, GoToMeeting, BlueJeans, and Whereby). ### Sync events from Google Calendar ``` GET https://api.meetstream.ai/api/v1/calendar/events ``` This is the primary events endpoint. It syncs with Google Calendar (using incremental sync for speed), stores events in MeetStream's database, and returns them with pagination and linked bot information. #### Query parameters | Parameter | Type | Default | Description | |---|---|---|---| | `calendar_id` | string | primary calendar | Specific calendar to sync | | `time_min` | ISO 8601 | now - 1 day | Start of the time window | | `time_max` | ISO 8601 | now + 28 days | End of the time window | | `sync` | `"true"` / `"false"` | auto | Force a sync from Google Calendar | | `limit` | int (1-100) | 50 | Number of events per page | | `cursor` | string | — | Pagination cursor from `next` field | | `cleanup_duplicates` | `"true"` / `"false"` | `"false"` | Deduplicate events | #### Example cURL ```bash curl -X GET "https://api.meetstream.ai/api/v1/calendar/events?limit=20" \ -H "Authorization: Token " ``` #### Response ```json { "next": "eyJFdmVudElEIjogIm...", "previous": null, "results": [ { "id": "evt_abc123", "start_time": "2026-04-08T15:00:00Z", "end_time": "2026-04-08T16:00:00Z", "calendar_id": "user@example.com", "platform": "google_calendar", "platform_id": "google_evt_456", "ical_uid": "abc123@google.com", "meeting_platform": "GMeet", "meeting_url": "https://meet.google.com/abc-defg-hij", "is_deleted": false, "created_at": "2026-04-01T12:00:00Z", "updated_at": "2026-04-07T09:30:00Z", "raw": { }, "bots": [ { "id": "bot_111", "status": "Scheduled", "scheduled_join_time": "2026-04-08T14:59:00+00:00", "bot_username": "MeetStream Calendar Bot", "platform": "GMeet", "is_scheduled": true } ] } ], "has_more": false } ``` Use the `next` cursor value in a subsequent request to fetch the next page: ```bash curl -X GET "https://api.meetstream.ai/api/v1/calendar/events?cursor=eyJFdmVudElEIjogIm..." \ -H "Authorization: Token " ``` ### Get events from local database only ``` GET https://api.meetstream.ai/api/v1/calendar/get_events ``` This is a lightweight endpoint that returns events already synced to MeetStream without calling the Google Calendar API. Useful when you want fast reads and don't need the latest sync. ```bash curl -X GET "https://api.meetstream.ai/api/v1/calendar/get_events" \ -H "Authorization: Token " ``` --- ## 5) Schedule a bot for a specific meeting Pick a meeting from your synced events and schedule a bot for it. ### API endpoint ``` POST https://api.meetstream.ai/api/v1/calendar/schedule/{event_id} ``` ### Path parameters | Parameter | Description | |---|---| | `event_id` | MeetStream event ID (the `id` field from the events list) | ### Request body ```json { "bot_config": { "bot_name": "My Meeting Bot", "audio_required": true, "video_required": false, "bot_message": "Bot joining to record this meeting", "callback_url": "https://your-domain.com/webhooks/meetstream", "transcription": { "deepgram": { "model": "nova-3", "language": "en" } }, "automatic_leave": { "no_one_joined_timeout": 300, "everyone_left_timeout": 60 }, "recording_config": { "video_recording": false } } } ``` The `bot_config` accepts the same fields you'd normally pass to the Create Bot API. ### Example cURL ```bash curl -X POST "https://api.meetstream.ai/api/v1/calendar/schedule/evt_abc123" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "bot_config": { "video_required": false, "callback_url": "https://your-domain.com/webhooks/meetstream" } }' ``` ### Response ```json { "scheduled": true, "schedule_id": "bot-usr_abc1-bot_111aaa12", "bot_id": "bot_111...", "schedule_group": "gmeet", "event_id": "evt_abc123", "scheduled_time": "2026-04-08T14:59:00Z", "bot_config": { }, "is_recurring_occurrence": false } ``` ### Scheduling options for recurring events | Body field | Type | Default | Description | |---|---|---|---| | `occurrence_date` | ISO 8601 | — | Schedule a bot for a specific occurrence of a recurring event | | `schedule_all_occurrences` | bool | `false` | Schedule bots for all future occurrences at once | | `occurrence_limit` | int | 52 | Maximum number of occurrences to schedule when using `schedule_all_occurrences` | | `recurring_event` | bool | `false` | Enable auto-rescheduling — after the bot joins this occurrence, automatically schedule the next one | ### Deduplication If you schedule a bot for the same event twice, MeetStream returns a `409 Conflict` with the existing bot's ID. To update the bot config, use `PATCH /calendar/scheduled_bots/{bot_id}` instead. --- ## 6) Unschedule a bot Cancel a scheduled bot for a specific event. ### API endpoint ``` DELETE https://api.meetstream.ai/api/v1/calendar/schedule/{event_id} ``` ### Request body (optional) ```json { "cancel_all_occurrences": false, "from_date": "2026-05-01T00:00:00Z" } ``` | Body field | Type | Default | Description | |---|---|---|---| | `cancel_all_occurrences` | bool | `false` | Cancel bots for all occurrences of a recurring series | | `from_date` | ISO 8601 | — | Only cancel occurrences from this date forward | ### Example cURL ```bash curl -X DELETE "https://api.meetstream.ai/api/v1/calendar/schedule/evt_abc123" \ -H "Authorization: Token " ``` ### Response ```json { "unscheduled": true, "event_id": "evt_abc123", "cancelled_schedules": ["bot-usr_abc1-bot_111aaa12"], "schedules_cancelled": 1, "bots_deleted": 1, "cancel_all_occurrences": false, "is_recurring_series": false } ``` --- ## 7) Manage scheduled bots View, update, or delete your scheduled bots across all events. ### List all scheduled bots ``` GET https://api.meetstream.ai/api/v1/calendar/scheduled_bots ``` Returns all bots with a scheduled join time in the future. #### Query parameters | Parameter | Type | Default | Description | |---|---|---|---| | `limit` | int (1-100) | 100 | Maximum number of bots to return | #### Example cURL ```bash curl -X GET "https://api.meetstream.ai/api/v1/calendar/scheduled_bots" \ -H "Authorization: Token " ``` #### Response ```json { "scheduled_bots": [ { "bot_id": "bot_111...", "platform": "GMeet", "status": "Scheduled", "scheduled_join_time": "2026-04-08T14:59:00+00:00", "bot_username": "MeetStream Calendar Bot", "meeting_link": "https://meet.google.com/abc-defg-hij", "custom_attributes": { "source": "calendar_integration", "event_id": "google_evt_456", "event_summary": "Weekly Standup" }, "is_scheduled": true, "created_at": "2026-04-07T10:00:00Z" } ] } ``` ### Update a scheduled bot ``` PATCH https://api.meetstream.ai/api/v1/calendar/scheduled_bots/{bot_id} ``` Update the join time, display name, or other properties of a scheduled bot. #### Request body All fields are optional — include only what you want to change. ```json { "scheduled_join_time": "2026-04-08T16:59:00Z", "bot_username": "Updated Bot Name", "custom_attributes": { "note": "VIP meeting" } } ``` | Body field | Type | Description | |---|---|---| | `scheduled_join_time` | ISO 8601 | New join time (must be in the future). Updates the EventBridge schedule too. | | `bot_username` | string | Display name for the bot in the meeting | | `custom_attributes` | object | Custom metadata attached to the bot | #### Response ```json { "message": "Scheduled bot updated successfully", "bot_id": "bot_111...", "updated_fields": ["scheduled_join_time", "bot_username"], "schedule_updated": true } ``` ### Delete a specific scheduled bot ``` DELETE https://api.meetstream.ai/api/v1/calendar/scheduled_bots/{bot_id} ``` ```bash curl -X DELETE "https://api.meetstream.ai/api/v1/calendar/scheduled_bots/bot_111..." \ -H "Authorization: Token " ``` ```json { "message": "Scheduled bot deleted successfully", "bot_id": "bot_111..." } ``` --- ## 8) Enable auto-scheduling Auto-scheduling is a hands-free mode: MeetStream scans your calendar every 24 hours and automatically schedules bots for all upcoming meetings that have a meeting link. ### Enable auto-scheduling ``` POST https://api.meetstream.ai/api/v1/calendar/auto-schedule/enable ``` ### Request body Provide a default bot configuration that will be used for all auto-scheduled bots: ```json { "default_bot_config": { "bot_name": "MeetStream Auto Bot", "audio_required": true, "video_required": false, "callback_url": "https://your-domain.com/webhooks/meetstream", "transcription": { "deepgram": { "model": "nova-3", "language": "en" } }, "automatic_leave": { "no_one_joined_timeout": 300, "everyone_left_timeout": 60 } } } ``` ### Example cURL ```bash curl -X POST "https://api.meetstream.ai/api/v1/calendar/auto-schedule/enable" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "default_bot_config": { "video_required": false, "callback_url": "https://your-domain.com/webhooks/meetstream" } }' ``` ### Response ```json { "message": "Auto-scheduling enabled successfully", "auto_schedule_enabled": true, "default_bot_config": { } } ``` ### Disable auto-scheduling ``` POST https://api.meetstream.ai/api/v1/calendar/auto-schedule/disable ``` ```bash curl -X POST "https://api.meetstream.ai/api/v1/calendar/auto-schedule/disable" \ -H "Authorization: Token " ``` ### Check auto-schedule settings ``` GET https://api.meetstream.ai/api/v1/calendar/auto-schedule/settings ``` ```json { "auto_schedule_enabled": true, "default_bot_config": { "bot_name": "MeetStream Auto Bot", "audio_required": true, "video_required": false } } ``` ### How auto-scheduling works 1. A background job runs **every 24 hours** at midnight UTC. 2. It finds all users who have auto-scheduling enabled. 3. For each user, it looks at upcoming events in the **next 24 hours** that have a valid meeting link. 4. It skips events that already have a bot scheduled (using deduplication keys). 5. It creates bot schedules using your `default_bot_config`. 6. Bots are scheduled to join **1 minute before** the meeting starts. > You can override individual meetings by manually scheduling them with `POST /calendar/schedule/{event_id}` and a custom `bot_config`. --- ## 9) Recurring event auto-rescheduling For recurring meetings (weekly standups, bi-weekly syncs, etc.), MeetStream can automatically schedule a bot for the **next occurrence** after each meeting ends. ### How it works 1. A bot joins a recurring meeting. 2. After the meeting ends, MeetStream detects it was a recurring event. 3. It calculates the next occurrence from the event's recurrence rule (RRULE). 4. A new bot is automatically scheduled for the next occurrence using the same configuration. This continues indefinitely — every recurring meeting gets a bot, without manual intervention. ### Enable auto-rescheduling when scheduling When scheduling a bot for a recurring event, set `recurring_event: true` in the request body: ```bash curl -X POST "https://api.meetstream.ai/api/v1/calendar/schedule/evt_abc123" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "bot_config": { "video_required": false }, "recurring_event": true }' ``` ### Toggle auto-rescheduling for an existing event You can enable or disable auto-rescheduling per event at any time: ``` POST https://api.meetstream.ai/api/v1/calendar/toggle-recurrence ``` ```json { "event_id": "evt_abc123", "recurring_enabled": true } ``` #### Response ```json { "event_id": "evt_abc123", "recurring_enabled": true, "recurrence_rule": "FREQ=WEEKLY;BYDAY=MO,WE,FR", "message": "Recurrence enabled for event", "summary": "Weekly Team Standup", "start_time": "2026-04-07T10:00:00Z", "end_time": "2026-04-07T10:30:00Z" } ``` > This endpoint returns 400 if the event is not actually a recurring event (no recurrence rule). ### Trigger rescheduling manually MeetStream also exposes an endpoint to manually trigger rescheduling for the next occurrence: ``` POST https://api.meetstream.ai/api/v1/calendar/auto-reschedule ``` ```json { "user_id": "usr_abc123", "event_id": "evt_abc123", "recurrence_rule": "FREQ=WEEKLY;BYDAY=MO,WE,FR", "current_start_time": "2026-04-07T10:00:00Z", "bot_config": { "video_required": false }, "meeting_link": "https://meet.google.com/abc-defg-hij" } ``` > This is primarily used internally by MeetStream after a meeting ends. You typically don't need to call it directly. ### Supported recurrence patterns MeetStream supports standard iCalendar recurrence rules (RRULE): | Pattern | RRULE example | |---|---| | Daily | `FREQ=DAILY` | | Weekly | `FREQ=WEEKLY;BYDAY=MO,WE,FR` | | Bi-weekly | `FREQ=WEEKLY;INTERVAL=2;BYDAY=TU` | | Monthly | `FREQ=MONTHLY;BYDAY=1MO` (first Monday) | | Yearly | `FREQ=YEARLY;BYMONTH=3;BYMONTHDAY=15` | --- ## 10) Real-time calendar change detection When you connect your calendar, MeetStream sets up **Google Calendar push notification channels** (watch channels) on all your calendars. This enables real-time reactions to calendar changes without polling. ### What MeetStream handles automatically | Calendar change | What MeetStream does | |---|---| | **Meeting rescheduled** (time changed) | Updates the bot's EventBridge schedule and `ScheduledJoinTime` to match the new time. The bot joins at the correct time. | | **Meeting cancelled or deleted** | Cancels the EventBridge schedule and marks the bot record as `Cancelled`. No bot is created. | | **Meeting time moved to the past** | Deletes the schedule and cancels the bot (since the meeting already happened or won't happen). | | **New meeting added** | If auto-scheduling is enabled, a bot is scheduled for it during the next auto-schedule run. | ### How it works under the hood 1. Google Calendar detects a change and sends an HTTPS POST to MeetStream's webhook endpoint. 2. MeetStream refreshes the event data from Google Calendar using incremental sync. 3. For each event that has an active bot scheduled: - **Single events:** The existing EventBridge schedule is updated in place with the new time. The bot record's `ScheduledJoinTime` is also updated so `GET /scheduled_bots` reflects the correct time. - **Recurring events:** All existing schedules are deleted and recreated with the updated recurrence times. - **Cancelled events:** The schedule is deleted and the bot record is marked `Cancelled`. You don't need to re-sync manually or make any API calls — changes are picked up in real time via webhooks from Google. ### Watch channel auto-renewal Google Calendar watch channels expire after approximately 30 days. MeetStream automatically renews expiring channels: - A background job runs **daily at 3:00 AM UTC**. - It checks all connected users' watch channels. - Any channel expiring within **2 days** is renewed automatically. - No user action is required — webhooks stay active indefinitely. --- ## 11) Disconnect your calendar To remove the calendar integration entirely: ### API endpoint ``` DELETE https://api.meetstream.ai/api/v1/calendar/disconnect ``` ### Example cURL ```bash curl -X DELETE "https://api.meetstream.ai/api/v1/calendar/disconnect" \ -H "Authorization: Token " ``` ### Response ```json { "disconnected": true, "user_id": "usr_abc123", "watch_channel_stopped": true, "events_deleted": 42, "schedules_cancelled": 5, "message": "Calendar disconnected successfully. All events and scheduled bots have been removed." } ``` ### What this does 1. Stops all Google Calendar watch channels (no more push notifications). 2. Cancels all pending EventBridge schedules and bot records. 3. Deletes all synced event data from MeetStream. 4. Removes stored Google OAuth credentials. > This is irreversible. To reconnect, call `POST /calendar/create_calendar` again with your credentials. --- ## End-to-end setup checklist Here's the full flow to get calendar integration running: 1. **Get credentials** — Create a Google Cloud project, enable the Calendar API, and run the OAuth helper to get your refresh token 2. **Connect** — `POST /calendar/create_calendar` with your Google OAuth credentials 3. **Sync** — `GET /calendar/events` to pull in your meetings 4. **Schedule** — `POST /calendar/schedule/{event_id}` to add a bot to a specific meeting 5. **Or auto-schedule** — `POST /calendar/auto-schedule/enable` to cover all meetings automatically 6. **Recurring** — Set `recurring_event: true` when scheduling, or use `POST /calendar/toggle-recurrence` 7. **Relax** — MeetStream handles calendar changes, rescheduling, cancellations, and watch channel renewal from here --- ## API quick reference | Method | Endpoint | Description | |---|---|---| | **Calendar connection** | | | | POST | `/api/v1/calendar/create_calendar` | Connect Google Calendar | | DELETE | `/api/v1/calendar/disconnect` | Disconnect calendar and clean up all data | | GET | `/api/v1/calendar/calendars` | List connected calendars (live from Google) | | **Events** | | | | GET | `/api/v1/calendar/events` | Sync and list events with pagination and bot info | | GET | `/api/v1/calendar/get_events` | List events from local database only (fast) | | **Bot scheduling** | | | | POST | `/api/v1/calendar/schedule/{event_id}` | Schedule a bot for an event | | DELETE | `/api/v1/calendar/schedule/{event_id}` | Unschedule a bot for an event | | **Scheduled bot management** | | | | GET | `/api/v1/calendar/scheduled_bots` | List all upcoming scheduled bots | | PATCH | `/api/v1/calendar/scheduled_bots/{bot_id}` | Update a scheduled bot (time, name, attributes) | | DELETE | `/api/v1/calendar/scheduled_bots/{bot_id}` | Delete a specific scheduled bot | | **Auto-scheduling** | | | | POST | `/api/v1/calendar/auto-schedule/enable` | Enable auto-scheduling with default bot config | | POST | `/api/v1/calendar/auto-schedule/disable` | Disable auto-scheduling | | GET | `/api/v1/calendar/auto-schedule/settings` | Get current auto-schedule settings | | **Recurring events** | | | | POST | `/api/v1/calendar/auto-reschedule` | Trigger rescheduling for next recurring occurrence | | POST | `/api/v1/calendar/toggle-recurrence` | Enable/disable auto-rescheduling per event | --- ## FAQ ### Which calendar providers are supported? Currently, **Google Calendar** is the only supported calendar provider. Your Google Calendar can contain meetings from any platform — MeetStream detects Google Meet, Zoom, Microsoft Teams, Webex, GoToMeeting, BlueJeans, and Whereby links. ### Do I need a Google Workspace account? No. Any Google account with Google Calendar works — personal Gmail accounts and Google Workspace accounts are both supported. ### How do I get a Google OAuth2 refresh token? Follow **Section 1** of this guide — create a Google Cloud project with the Calendar API enabled, set up OAuth 2.0 credentials, and run the provided OAuth helper (`node server.js`) to complete the consent flow. The helper displays your refresh token in the browser. ### How far in advance does auto-scheduling look? The auto-schedule job runs every 24 hours at midnight UTC and schedules bots for meetings happening in the **next 24 hours**. Meetings further out will be picked up in subsequent runs. ### When does the bot join relative to the meeting start time? Bots are scheduled to join **1 minute before** the meeting's start time. ### What happens if I reschedule a meeting in Google Calendar? MeetStream receives a real-time push notification from Google and automatically updates the bot's scheduled join time to match the new meeting time. Both the EventBridge schedule and the bot record in MeetStream's database are updated, so `GET /scheduled_bots` always shows the correct time. ### What if I cancel a meeting? MeetStream detects the cancellation via Google Calendar sync, deletes the EventBridge schedule, and marks the bot as `Cancelled`. No bot will be created for a cancelled meeting. ### What if a meeting is moved to a time that already passed? MeetStream detects that the new time is in the past, deletes the EventBridge schedule, and cancels the bot record. This prevents a bot from being created for a meeting that can no longer be joined. ### Can I schedule bots for meetings without a meeting link? No. MeetStream requires a valid meeting link (Google Meet, Zoom, Teams, etc.) to join. Events without a detected meeting link are skipped. ### What happens if I schedule a bot for the same event twice? MeetStream deduplicates by event. The second call returns a `409 Conflict` with the existing bot's ID. To update the bot config, use `PATCH /calendar/scheduled_bots/{bot_id}`. ### Can I use different bot configurations for different meetings? Yes. When you manually schedule a bot via `POST /calendar/schedule/{event_id}`, you provide the `bot_config` per event. Auto-scheduling uses your `default_bot_config` for all meetings, but you can override individual events by scheduling them manually. ### Does auto-rescheduling work with all recurrence patterns? MeetStream supports standard iCalendar recurrence rules (RRULE) — daily, weekly, bi-weekly, monthly, yearly, and custom patterns. The next occurrence is calculated from the event's recurrence rule. ### How do I stop a recurring event from being rescheduled? Use the toggle endpoint: `POST /calendar/toggle-recurrence` with `"recurring_enabled": false` for that event. ### Do I need to worry about watch channels expiring? No. MeetStream automatically renews Google Calendar watch channels before they expire. A daily background job checks for channels expiring within 2 days and renews them. Your real-time calendar sync stays active indefinitely without any action on your part. ### What data is deleted when I disconnect my calendar? Everything: watch channels are stopped, pending bot schedules are cancelled, synced event data is deleted, and Google OAuth credentials are removed from storage. This is irreversible. ### Is my Google Calendar data stored securely? Yes. OAuth credentials are stored in AWS Systems Manager Parameter Store as encrypted `SecureString` parameters. Event data is stored in DynamoDB with encryption at rest. MeetStream does not store your Google password. ### Can I schedule bots for all occurrences of a recurring event at once? Yes. Pass `"schedule_all_occurrences": true` in the request body when scheduling. You can limit the number of occurrences with `"occurrence_limit"` (default 52). Each occurrence gets its own bot and EventBridge schedule. ### How do I schedule a bot for a specific occurrence of a recurring event? Pass `"occurrence_date": "2026-04-14T10:00:00Z"` in the request body. MeetStream will schedule a bot for that specific occurrence only. ### Can I cancel bots for future occurrences only? Yes. When unscheduling a recurring event, pass `"from_date": "2026-05-01T00:00:00Z"` to cancel only occurrences from that date forward. Occurrences before that date keep their scheduled bots. --- For webhook event handling, see the [Webhook Events Guide](https://docs.meetstream.ai/guides/webhooks/webhooks-and-events). For creating your first bot without calendar integration, see the [First Bot Quickstart](https://docs.meetstream.ai/guides/get-started/create-your-first-bot). # MeetStream Guide: Outlook Calendar Integration & Auto-Scheduling This guide explains how to connect Microsoft Outlook / Microsoft 365 Calendar accounts to MeetStream so bots can automatically join your meetings — no manual API calls required. Applies to: **Google Meet, Zoom, Microsoft Teams** meetings on your Outlook Calendar. Support: docs.meetstream.ai | API: api.meetstream.ai --- ## What you get with Calendar Integration Once connected, MeetStream can: - **Connect many Microsoft accounts per user** — one MeetStream user can attach a personal Outlook.com account, a work Microsoft 365 tenant, a contractor account in a third tenant, and so on. Up to 200 per user (Google + Outlook combined). - **See upcoming meetings across every connected account** — synced directly from Microsoft Graph with per-account delta tokens for speed. - **Schedule bots for specific meetings** — one click or one API call. - **Auto-schedule bots for all meetings** — hands-free, every day, across every connected account. - **Handle recurring meetings** — automatically reschedule bots for the next occurrence. - **React to calendar changes in real time** — if a meeting is rescheduled, the bot's join time updates automatically; if a meeting is cancelled, the bot is cancelled too. Notifications are routed per-account so one tenant's churn never disturbs another's. - **Manage scheduled bots** — list, update, or delete scheduled bots at any time. - **Disconnect one account at a time** — remove a single Microsoft account without touching the others. - **Keep subscriptions alive** — Microsoft Graph change notification subscriptions are automatically renewed before they expire. > **Mental model.** A MeetStream user owns a set of **calendar connections**. Each connection is uniquely identified by a `(provider, account_id)` pair — for Outlook, `account_id` is the account's primary email (e.g. `jane@acme.com`) as returned by Microsoft Graph's `me` endpoint. Every endpoint in this guide either operates across **all** of a MeetStream user's connections (the default) or scopes to a single connection via `?provider=&account_id=` query parameters or a path parameter. The provider key is `outlook` everywhere; the alias `microsoft` is accepted too. If you only ever connect one Outlook account per MeetStream user, the multi-account surface is invisible to you — everything just works. --- ## 1) Get your Microsoft OAuth credentials Before connecting your calendar, you need three things from Microsoft: a **Client ID**, **Client Secret**, and **Refresh Token**. Follow these steps to get them. ### Step 1: Register an app in Azure Portal 1. Go to the [Azure Portal](https://portal.azure.com/). 2. Navigate to **Azure Active Directory > App registrations**. 3. Click **New registration**. 4. Give your app a name (e.g. "MeetStream Calendar"). 5. Under **Supported account types**, select **Accounts in any organizational directory and personal Microsoft accounts**. 6. Under **Redirect URI**, select **Web** and enter: ``` http://localhost:3000/api/microsoft/oauth-callback ``` 7. Click **Register**. 8. On the app overview page, copy your **Application (client) ID** — this is your **Client ID**. ### Step 2: Create a client secret 1. In your app registration, go to **Certificates & secrets**. 2. Click **New client secret**. 3. Give it a description and choose an expiry period. 4. Click **Add** and immediately copy the **Value** — this is your **Client Secret**. It won't be shown again. ### Step 3: Add Microsoft Graph API permissions 1. In your app registration, go to **API permissions**. 2. Click **Add a permission > Microsoft Graph > Delegated permissions**. 3. Add the following permissions: | Permission | Purpose | |---|---| | `Calendars.Read` | Read calendar events | | `User.Read` | Read user profile and email | | `offline_access` | Obtain refresh tokens for long-lived access | 4. Click **Grant admin consent** if you have admin rights, or ask your tenant admin to grant consent. ### Step 4: Run the OAuth helper to get your refresh token MeetStream provides a lightweight Node.js helper that runs the OAuth consent flow locally and returns your refresh token. **Prerequisites:** Node.js installed on your machine. 1. Create a `.env` file in the project root with your credentials: ``` MICROSOFT_CLIENT_ID= MICROSOFT_CLIENT_SECRET= ``` 2. Install dependencies and start the helper server: ```bash npm install express @azure/msal-node dotenv node server_microsoft.js ``` You'll see: ``` Open http://localhost:3000 to start the OAuth flow Redirect URI: http://localhost:3000/api/microsoft/oauth-callback Make sure this redirect URI is added in Azure Portal → App registrations → Authentication → Redirect URIs ``` 3. Open **http://localhost:3000** in your browser. 4. Sign in with your Microsoft account and grant calendar access. 5. After authorization, the page displays your **Refresh Token** and **Access Token**. 6. Copy the **Refresh Token** — you'll need it in the next step. > If the refresh token is not returned, make sure `offline_access` is included in your permissions and that you clicked **Accept** on the consent screen. Microsoft only returns a refresh token when `offline_access` is in the requested scope. ### Required scopes The helper requests these scopes: | Scope | Purpose | |---|---| | `Calendars.Read` | Read calendar events and calendar list | | `User.Read` | Identify the Microsoft account and profile | | `offline_access` | Obtain long-lived refresh tokens | You now have everything you need: **Client ID**, **Client Secret**, and **Refresh Token**. --- ## 2) Connect an Outlook Calendar account Each call to this endpoint connects **one** Microsoft account. To attach a second, third, or twentieth account to the same MeetStream user, call it again with a different refresh token. MeetStream resolves the account from Microsoft Graph's `me` endpoint and stores each connection separately. ### API endpoint ``` POST https://api.meetstream.ai/api/v1/calendar/create_outlook_calendar ``` ### Request body ```json { "microsoft_client_id": "", "microsoft_client_secret": "", "microsoft_refresh_token": "" } ``` Optional: | Field | Type | Default | Description | |---|---|---|---| | `replace` | bool | `false` | When `true`, allows the call to overwrite an existing connection for the same `account_id` (token rotation, scope refresh). Without this flag, reconnecting an already-connected account returns **409 Conflict**. | ### What happens behind the scenes 1. MeetStream exchanges the refresh token at Microsoft's OAuth endpoint to validate the credentials. 2. Calls Microsoft Graph's `me` endpoint to resolve the account's primary email — that email becomes the `account_id` for this connection. 3. Fetches all calendars on that account (default, secondary, shared) via Microsoft Graph. 4. Stores your credentials securely (encrypted at rest in AWS SSM Parameter Store) under a per-account path, isolated from any other accounts you've connected. 5. Registers **Microsoft Graph change notification subscriptions** on every calendar on that account, pointing at a per-account webhook URL with a per-subscription `clientState` secret so notifications for this account never cross-contaminate any other connection on the same user. 6. Returns the connection record (no token material). ### Example cURL ```bash curl -X POST "https://api.meetstream.ai/api/v1/calendar/create_outlook_calendar" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "microsoft_client_id": "", "microsoft_client_secret": "", "microsoft_refresh_token": "" }' ``` ### Response ```json { "calendar_id": "outlook_calendar_usr_abc123", "platform": "outlook_calendar", "provider": "outlook", "account_id": "jane@acme.com", "user_email": "jane@acme.com", "user_name": "Jane Doe", "calendars": [ { "id": "AQMkADAwATM0MDAA...", "summary": "Calendar", "isPrimary": true, "accessRole": "owner", "selected": true }, { "id": "AQMkADAwATM0MDAB...", "summary": "Team Meetings", "isPrimary": false, "accessRole": "writer", "selected": true } ], "primary_calendar_id": "AQMkADAwATM0MDAA...", "watch_setup": { "success": true, "subscriptions_setup": 2, "subscription_results": [ { "calendar_id": "AQMkADAwATM0MDAA...", "calendar_summary": "Calendar", "subscription_id": "a1b2c3d4-...", "expiration": "2026-05-23T22:00:00Z" } ], "failed_calendars": [] }, "message": "Calendar connected successfully" } ``` ### Connecting a second account Run the OAuth helper again signed in as the second Microsoft account, get a new refresh token, then call `POST /create_outlook_calendar` again with that token. The new account is added to the MeetStream user; existing connections — Outlook or Google — are untouched. There's no separate "add account" endpoint, and there's no provider mode: the same endpoint handles both Microsoft and Google credentials based on which fields you send. ```bash # Connect a second Outlook account (e.g. a work tenant) to the same MeetStream user. curl -X POST "https://api.meetstream.ai/api/v1/calendar/create_outlook_calendar" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "microsoft_client_id": "", "microsoft_client_secret": "", "microsoft_refresh_token": "" }' ``` ### Reconnecting an existing account (token rotation) If the same `account_id` is already connected for this MeetStream user, the call fails with **409 Conflict** by default: ```json { "error": "Outlook account 'jane@acme.com' is already connected for this user. Pass {\"replace\": true} in the request body to reconnect." } ``` Pass `"replace": true` in the body to rotate the refresh token / refresh the scopes for an already-connected account in place: ```json { "microsoft_client_id": "...", "microsoft_client_secret": "...", "microsoft_refresh_token": "", "replace": true } ``` This is the right flow when your Azure client secret rotated or when you've extended the granted scopes — the credentials are updated without losing event history or notification subscriptions for that account. ### Limits | Limit | Default | |---|---| | Calendar connections per MeetStream user (Google + Outlook combined) | **200** | At the limit, `POST /create_outlook_calendar` returns `400` with `"Maximum of 200 outlook calendar connections per user reached…"`. Disconnect one before adding another (see §11), or contact support to raise the limit. --- ## 3) View your connections and calendars You have two read endpoints: one for the **connections** themselves (which Microsoft accounts are attached, with metadata) and one for the **calendars within those connections** (live from Graph). ### 3a) List your calendar connections ``` GET https://api.meetstream.ai/api/v1/calendar/connections ``` Returns every connection on the authenticated MeetStream user, grouped by provider. No token material is ever returned. ```bash curl -X GET "https://api.meetstream.ai/api/v1/calendar/connections" \ -H "Authorization: Token " ``` ```json { "user_id": "usr_abc123", "google": [], "outlook": [ { "account_id": "jane@acme.com", "email": "jane@acme.com", "display_name": "Jane Doe (work)", "primary_calendar_id": "AQMkADAwATM0MDAA...", "scopes": ["Calendars.Read", "User.Read", "offline_access"], "created_at": "2026-04-10T11:20:00Z", "updated_at": "2026-04-10T11:20:00Z", "calendars_count": 12, "subscriptions_count": 12 }, { "account_id": "jane.personal@outlook.com", "email": "jane.personal@outlook.com", "display_name": "Jane Doe (personal)", "primary_calendar_id": "AQMkADAwATM0XYZ...", "scopes": ["Calendars.Read", "User.Read", "offline_access"], "created_at": "2026-04-12T09:05:00Z", "updated_at": "2026-04-12T09:05:00Z", "calendars_count": 4, "subscriptions_count": 4 } ], "total": 2 } ``` #### Inspect one connection ``` GET https://api.meetstream.ai/api/v1/calendar/connections/{provider}/{account_id} ``` `{provider}` is `outlook` (the alias `microsoft` is accepted). `{account_id}` must be URL-encoded (`@` becomes `%40`, etc.). ```bash curl -X GET "https://api.meetstream.ai/api/v1/calendar/connections/outlook/jane%40acme.com" \ -H "Authorization: Token " ``` Returns a single connection record like one of the entries above. **404** if the user has no connection for that account. ### 3b) List the calendars inside those connections ``` GET https://api.meetstream.ai/api/v1/calendar/calendars ``` Returns calendars across **every** connected account by default (Outlook + Google), with a per-connection summary so you can group them in your UI. #### Query parameters | Parameter | Required when… | Example | |---|---|---| | `provider` | You want to filter by provider only | `?provider=outlook` | | `account_id` | You want only one specific account's calendars. **Requires `provider`**. | `?provider=outlook&account_id=jane%40acme.com` | #### Example cURL ```bash # All calendars from every connected account (Outlook + Google) curl -X GET "https://api.meetstream.ai/api/v1/calendar/calendars" \ -H "Authorization: Token " # Only calendars from one specific Outlook account curl -X GET "https://api.meetstream.ai/api/v1/calendar/calendars?provider=outlook&account_id=jane%40acme.com" \ -H "Authorization: Token " ``` #### Response ```json { "calendars": [ { "id": "AQMkADAwATM0MDAA...", "summary": "Calendar", "isPrimary": true, "accessRole": "owner", "timeZone": "America/New_York", "backgroundColor": "#0078d4", "selected": true, "provider": "outlook", "account_id": "jane@acme.com" }, { "id": "AQMkADAwATM0XYZ...", "summary": "Personal", "isPrimary": true, "accessRole": "owner", "timeZone": "America/New_York", "backgroundColor": "#0078d4", "selected": true, "provider": "outlook", "account_id": "jane.personal@outlook.com" } ], "connections": [ { "provider": "outlook", "account_id": "jane@acme.com", "calendars_count": 1, "is_legacy": false }, { "provider": "outlook", "account_id": "jane.personal@outlook.com", "calendars_count": 1, "is_legacy": false } ], "total": 2, "user_id": "usr_abc123" } ``` > This is a live call to the Microsoft Graph API and always returns the latest calendar list. Each entry carries `provider` and `account_id` so the dashboard / client can tell which connection it came from. > > If one connection fails to respond (revoked token, Graph throttling, expired client secret), the call still succeeds and surfaces the error under a `partial_errors` map keyed by `"{provider}::{account_id}"`. --- ## 4) Sync and view your events MeetStream syncs events from every connected Outlook Calendar account and stores them locally. It detects meeting links for supported platforms (Google Meet, Zoom, Microsoft Teams, Webex, GoToMeeting, BlueJeans, and Whereby). ### Sync events from Outlook Calendar ``` GET https://api.meetstream.ai/api/v1/calendar/events ``` This is the primary events endpoint. It syncs with Outlook Calendar (using per-account Microsoft Graph delta tokens for speed), stores events in MeetStream's database, and returns them with pagination and linked bot information. By default, events from **every** connected account on the MeetStream user (Outlook + Google) are returned. #### Query parameters | Parameter | Type | Default | Description | |---|---|---|---| | `calendar_id` | string | primary calendar of each connection | Specific calendar to sync | | `time_min` | ISO 8601 | now - 1 day | Start of the time window | | `time_max` | ISO 8601 | now + 28 days | End of the time window | | `sync` | `"true"` / `"false"` | auto | Force a sync from Outlook Calendar | | `limit` | int (1-100) | 50 | Number of events per page | | `cursor` | string | — | Pagination cursor from `next` field | | `cleanup_duplicates` | `"true"` / `"false"` | `"false"` | Deduplicate events | | `provider` | `"outlook"` / `"google"` | all | Scope to one provider | | `account_id` | string | all accounts | Scope to one specific connection. **Requires `provider`.** Use to fetch events from just one Outlook account when the user has several connected. | #### Example cURL ```bash curl -X GET "https://api.meetstream.ai/api/v1/calendar/events?limit=20" \ -H "Authorization: Token " ``` #### Response ```json { "next": "eyJFdmVudElEIjogIm...", "previous": null, "results": [ { "id": "evt_abc123", "start_time": "2026-04-08T15:00:00Z", "end_time": "2026-04-08T16:00:00Z", "calendar_id": "AQMkADAwATM0MDAA...", "platform": "outlook_calendar", "provider": "outlook", "account_id": "jane@acme.com", "platform_id": "outlook_evt_456", "ical_uid": "abc123@outlook.com", "meeting_platform": "Teams", "meeting_url": "https://teams.microsoft.com/l/meetup-join/...", "is_deleted": false, "created_at": "2026-04-01T12:00:00Z", "updated_at": "2026-04-07T09:30:00Z", "raw": { }, "bots": [ { "id": "bot_111", "status": "Scheduled", "scheduled_join_time": "2026-04-08T14:59:00+00:00", "bot_username": "MeetStream Calendar Bot", "platform": "Teams", "is_scheduled": true } ] } ], "has_more": false } ``` Every event row carries a `provider` and `account_id` so you can group / filter client-side without re-issuing scoped requests. Use the `next` cursor value in a subsequent request to fetch the next page: ```bash curl -X GET "https://api.meetstream.ai/api/v1/calendar/events?cursor=eyJFdmVudElEIjogIm..." \ -H "Authorization: Token " ``` ### Scoping to a single account When the user has multiple connections, you can scope the sync + return to just one of them. This is faster (only one account is touched) and useful for per-tenant UIs. ```bash curl -X GET "https://api.meetstream.ai/api/v1/calendar/events?provider=outlook&account_id=jane%40acme.com&limit=20" \ -H "Authorization: Token " ``` ### Get events from local database only ``` GET https://api.meetstream.ai/api/v1/calendar/get_events ``` This is a lightweight endpoint that returns events already synced to MeetStream without calling the Microsoft Graph API. Useful when you want fast reads and don't need the latest sync. Accepts the same `provider` / `account_id` scoping query parameters as `/calendar/events`. ```bash # All accounts (default) curl -X GET "https://api.meetstream.ai/api/v1/calendar/get_events" \ -H "Authorization: Token " # One specific account curl -X GET "https://api.meetstream.ai/api/v1/calendar/get_events?provider=outlook&account_id=jane%40acme.com" \ -H "Authorization: Token " ``` --- ## 5) Schedule a bot for a specific meeting Pick a meeting from your synced events and schedule a bot for it. ### API endpoint ``` POST https://api.meetstream.ai/api/v1/calendar/schedule/{event_id} ``` ### Path parameters | Parameter | Description | |---|---| | `event_id` | MeetStream event ID (the `id` field from the events list) | ### Request body ```json { "bot_config": { "bot_name": "My Meeting Bot", "audio_required": true, "video_required": false, "bot_message": "Bot joining to record this meeting", "callback_url": "https://your-domain.com/webhooks/meetstream", "transcription": { "deepgram": { "model": "nova-3", "language": "en" } }, "automatic_leave": { "no_one_joined_timeout": 300, "everyone_left_timeout": 60 }, "recording_config": { "video_recording": false } } } ``` The `bot_config` accepts the same fields you'd normally pass to the Create Bot API. ### Example cURL ```bash curl -X POST "https://api.meetstream.ai/api/v1/calendar/schedule/evt_abc123" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "bot_config": { "video_required": false, "callback_url": "https://your-domain.com/webhooks/meetstream" } }' ``` ### Response ```json { "scheduled": true, "schedule_id": "bot-usr_abc1-bot_111aaa12", "bot_id": "bot_111...", "schedule_group": "teams", "event_id": "evt_abc123", "scheduled_time": "2026-04-08T14:59:00Z", "bot_config": { }, "is_recurring_occurrence": false } ``` ### Scheduling options for recurring events | Body field | Type | Default | Description | |---|---|---|---| | `occurrence_date` | ISO 8601 | — | Schedule a bot for a specific occurrence of a recurring event | | `schedule_all_occurrences` | bool | `false` | Schedule bots for all future occurrences at once | | `occurrence_limit` | int | 52 | Maximum number of occurrences to schedule when using `schedule_all_occurrences` | | `recurring_event` | bool | `false` | Enable auto-rescheduling — after the bot joins this occurrence, automatically schedule the next one | ### Deduplication If you schedule a bot for the same event twice, MeetStream returns a `409 Conflict` with the existing bot's ID. To update the bot config, use `PATCH /calendar/scheduled_bots/{bot_id}` instead. --- ## 6) Unschedule a bot Cancel a scheduled bot for a specific event. ### API endpoint ``` DELETE https://api.meetstream.ai/api/v1/calendar/schedule/{event_id} ``` ### Request body (optional) ```json { "cancel_all_occurrences": false, "from_date": "2026-05-01T00:00:00Z" } ``` | Body field | Type | Default | Description | |---|---|---|---| | `cancel_all_occurrences` | bool | `false` | Cancel bots for all occurrences of a recurring series | | `from_date` | ISO 8601 | — | Only cancel occurrences from this date forward | ### Example cURL ```bash curl -X DELETE "https://api.meetstream.ai/api/v1/calendar/schedule/evt_abc123" \ -H "Authorization: Token " ``` ### Response ```json { "unscheduled": true, "event_id": "evt_abc123", "cancelled_schedules": ["bot-usr_abc1-bot_111aaa12"], "schedules_cancelled": 1, "bots_deleted": 1, "cancel_all_occurrences": false, "is_recurring_series": false } ``` --- ## 7) Manage scheduled bots View, update, or delete your scheduled bots across all events. ### List all scheduled bots ``` GET https://api.meetstream.ai/api/v1/calendar/scheduled_bots ``` Returns all bots with a scheduled join time in the future. #### Query parameters | Parameter | Type | Default | Description | |---|---|---|---| | `limit` | int (1-100) | 100 | Maximum number of bots to return | #### Example cURL ```bash curl -X GET "https://api.meetstream.ai/api/v1/calendar/scheduled_bots" \ -H "Authorization: Token " ``` #### Response ```json { "scheduled_bots": [ { "bot_id": "bot_111...", "platform": "Teams", "status": "Scheduled", "scheduled_join_time": "2026-04-08T14:59:00+00:00", "bot_username": "MeetStream Calendar Bot", "meeting_link": "https://teams.microsoft.com/l/meetup-join/...", "custom_attributes": { "source": "calendar_integration", "event_id": "outlook_evt_456", "event_summary": "Weekly Standup" }, "is_scheduled": true, "created_at": "2026-04-07T10:00:00Z" } ] } ``` ### Update a scheduled bot ``` PATCH https://api.meetstream.ai/api/v1/calendar/scheduled_bots/{bot_id} ``` Update the join time, display name, or other properties of a scheduled bot. #### Request body All fields are optional — include only what you want to change. ```json { "scheduled_join_time": "2026-04-08T16:59:00Z", "bot_username": "Updated Bot Name", "custom_attributes": { "note": "VIP meeting" } } ``` | Body field | Type | Description | |---|---|---| | `scheduled_join_time` | ISO 8601 | New join time (must be in the future). Updates the EventBridge schedule too. | | `bot_username` | string | Display name for the bot in the meeting | | `custom_attributes` | object | Custom metadata attached to the bot | #### Response ```json { "message": "Scheduled bot updated successfully", "bot_id": "bot_111...", "updated_fields": ["scheduled_join_time", "bot_username"], "schedule_updated": true } ``` ### Delete a specific scheduled bot ``` DELETE https://api.meetstream.ai/api/v1/calendar/scheduled_bots/{bot_id} ``` ```bash curl -X DELETE "https://api.meetstream.ai/api/v1/calendar/scheduled_bots/bot_111..." \ -H "Authorization: Token " ``` ```json { "message": "Scheduled bot deleted successfully", "bot_id": "bot_111..." } ``` --- ## 8) Enable auto-scheduling Auto-scheduling is a hands-free mode: MeetStream scans your calendar every 24 hours and automatically schedules bots for all upcoming meetings that have a meeting link. ### Enable auto-scheduling ``` POST https://api.meetstream.ai/api/v1/calendar/auto-schedule/enable ``` ### Request body Provide a default bot configuration that will be used for all auto-scheduled bots: ```json { "default_bot_config": { "bot_name": "MeetStream Auto Bot", "audio_required": true, "video_required": false, "callback_url": "https://your-domain.com/webhooks/meetstream", "transcription": { "deepgram": { "model": "nova-3", "language": "en" } }, "automatic_leave": { "no_one_joined_timeout": 300, "everyone_left_timeout": 60 } } } ``` ### Example cURL ```bash curl -X POST "https://api.meetstream.ai/api/v1/calendar/auto-schedule/enable" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "default_bot_config": { "video_required": false, "callback_url": "https://your-domain.com/webhooks/meetstream" } }' ``` ### Response ```json { "message": "Auto-scheduling enabled successfully", "auto_schedule_enabled": true, "default_bot_config": { } } ``` ### Disable auto-scheduling ``` POST https://api.meetstream.ai/api/v1/calendar/auto-schedule/disable ``` ```bash curl -X POST "https://api.meetstream.ai/api/v1/calendar/auto-schedule/disable" \ -H "Authorization: Token " ``` ### Check auto-schedule settings ``` GET https://api.meetstream.ai/api/v1/calendar/auto-schedule/settings ``` ```json { "auto_schedule_enabled": true, "default_bot_config": { "bot_name": "MeetStream Auto Bot", "audio_required": true, "video_required": false } } ``` ### How auto-scheduling works 1. A background job runs **every 24 hours** at midnight UTC. 2. It finds all users who have auto-scheduling enabled. 3. For each user, it looks at upcoming events in the **next 24 hours** that have a valid meeting link. 4. It skips events that already have a bot scheduled (using deduplication keys). 5. It creates bot schedules using your `default_bot_config`. 6. Bots are scheduled to join **1 minute before** the meeting starts. > You can override individual meetings by manually scheduling them with `POST /calendar/schedule/{event_id}` and a custom `bot_config`. --- ## 9) Recurring event auto-rescheduling For recurring meetings (weekly standups, bi-weekly syncs, etc.), MeetStream can automatically schedule a bot for the **next occurrence** after each meeting ends. ### How it works 1. A bot joins a recurring meeting. 2. After the meeting ends, MeetStream detects it was a recurring event. 3. It calculates the next occurrence from the event's recurrence rule. 4. A new bot is automatically scheduled for the next occurrence using the same configuration. This continues indefinitely — every recurring meeting gets a bot, without manual intervention. ### Enable auto-rescheduling when scheduling When scheduling a bot for a recurring event, set `recurring_event: true` in the request body: ```bash curl -X POST "https://api.meetstream.ai/api/v1/calendar/schedule/evt_abc123" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "bot_config": { "video_required": false }, "recurring_event": true }' ``` ### Toggle auto-rescheduling for an existing event You can enable or disable auto-rescheduling per event at any time: ``` POST https://api.meetstream.ai/api/v1/calendar/toggle-recurrence ``` ```json { "event_id": "evt_abc123", "recurring_enabled": true } ``` #### Response ```json { "event_id": "evt_abc123", "recurring_enabled": true, "recurrence_rule": "FREQ=WEEKLY;BYDAY=MO,WE,FR", "message": "Recurrence enabled for event", "summary": "Weekly Team Standup", "start_time": "2026-04-07T10:00:00Z", "end_time": "2026-04-07T10:30:00Z" } ``` > This endpoint returns 400 if the event is not actually a recurring event (no recurrence rule). ### Trigger rescheduling manually MeetStream also exposes an endpoint to manually trigger rescheduling for the next occurrence: ``` POST https://api.meetstream.ai/api/v1/calendar/auto-reschedule ``` ```json { "user_id": "usr_abc123", "event_id": "evt_abc123", "recurrence_rule": "FREQ=WEEKLY;BYDAY=MO,WE,FR", "current_start_time": "2026-04-07T10:00:00Z", "bot_config": { "video_required": false }, "meeting_link": "https://teams.microsoft.com/l/meetup-join/..." } ``` > This is primarily used internally by MeetStream after a meeting ends. You typically don't need to call it directly. ### Supported recurrence patterns MeetStream supports standard iCalendar recurrence rules (RRULE) as surfaced by Microsoft Graph: | Pattern | RRULE example | |---|---| | Daily | `FREQ=DAILY` | | Weekly | `FREQ=WEEKLY;BYDAY=MO,WE,FR` | | Bi-weekly | `FREQ=WEEKLY;INTERVAL=2;BYDAY=TU` | | Monthly | `FREQ=MONTHLY;BYDAY=1MO` (first Monday) | | Yearly | `FREQ=YEARLY;BYMONTH=3;BYMONTHDAY=15` | --- ## 10) Real-time calendar change detection When you connect your calendar, MeetStream sets up **Microsoft Graph change notification subscriptions** on your calendars. This enables real-time reactions to calendar changes without polling. ### What MeetStream handles automatically | Calendar change | What MeetStream does | |---|---| | **Meeting rescheduled** (time changed) | Updates the bot's EventBridge schedule and `ScheduledJoinTime` to match the new time. The bot joins at the correct time. | | **Meeting cancelled or deleted** | Cancels the EventBridge schedule and marks the bot record as `Cancelled`. No bot is created. | | **Meeting time moved to the past** | Deletes the schedule and cancels the bot (since the meeting already happened or won't happen). | | **New meeting added** | If auto-scheduling is enabled, a bot is scheduled for it during the next auto-schedule run. | ### How it works under the hood 1. Microsoft Graph detects a change and sends an HTTPS POST to a **per-account** webhook URL of the form `…/api/v1/admin/schedule/users/{user_id}/{provider}/{account_id}` — so a notification for `jane@acme.com` cannot be confused with a notification for `jane.personal@outlook.com` even when both are connected to the same MeetStream user. 2. MeetStream validates the notification using the per-subscription `clientState` secret issued at registration time and rejects mismatches. 3. MeetStream fetches updated event data from the Microsoft Graph API using the **per-account** delta token (one token per `account_id`, isolated from every other account's sync state). 4. For each event that has an active bot scheduled: - **Single events:** The existing EventBridge schedule is updated in place with the new time. The bot record's `ScheduledJoinTime` is also updated so `GET /scheduled_bots` reflects the correct time. - **Recurring events:** All existing schedules are deleted and recreated with the updated recurrence times. - **Cancelled events:** The schedule is deleted and the bot record is marked `Cancelled`. Only events from the account that fired the notification are touched. Changes on other connections wait for their own notifications. You don't need to re-sync manually or make any API calls — changes are picked up in real time via webhooks from Microsoft. ### Subscription auto-renewal Microsoft Graph change notification subscriptions for calendar events expire after approximately **3 days** (4,230 minutes). MeetStream automatically renews expiring subscriptions: - A background job runs **daily at 3:00 AM UTC**. - It checks all connected users' Graph subscriptions. - Any subscription expiring within **2 days** is renewed automatically. - No user action is required — webhooks stay active indefinitely. > This is more frequent than Google Calendar's 30-day watch channels. MeetStream handles the renewal schedule transparently — you will not notice any gap in change detection. --- ## 11) Disconnect a calendar account There are two disconnect endpoints. **For multi-account users, prefer the per-account variant** — it's explicit about which account is being removed and leaves every other connection alone. ### 11a) Disconnect one account (recommended) ``` DELETE https://api.meetstream.ai/api/v1/calendar/connections/{provider}/{account_id} ``` `{provider}` is `outlook` (the alias `microsoft` is accepted). `{account_id}` must be URL-encoded (`@` → `%40`). #### What this does 1. Stops the Microsoft Graph change notification subscriptions registered for this connection (no more push notifications). 2. Deletes the per-account OAuth credentials from secure storage. 3. Removes the connection record from `GET /calendar/connections`. 4. **Cleans up events and scheduled bots that came from this account** — `Events` rows tagged with this `(provider, account_id)` are deleted; bots in `Scheduled` status for those events move to `Cancelled` with `reason: "calendar_disconnected"`. Bots that have already run are not touched. Other Outlook (and Google) connections on this MeetStream user are completely unaffected. #### Query parameter | Parameter | Default | Description | |---|---|---| | `purge_events` | `true` | Set to `false` to disconnect the account without deleting its event history. The credentials and subscriptions are still removed (no more updates), but historical event rows stay in the database. | #### Example cURL ```bash # Disconnect the work account and purge its events + scheduled bots. curl -X DELETE "https://api.meetstream.ai/api/v1/calendar/connections/outlook/jane%40acme.com" \ -H "Authorization: Token " # Disconnect but keep the event history for reporting. curl -X DELETE "https://api.meetstream.ai/api/v1/calendar/connections/outlook/jane%40acme.com?purge_events=false" \ -H "Authorization: Token " ``` #### Response ```json { "found": true, "disconnected": true, "provider": "outlook", "account_id": "jane@acme.com", "purge_events": true, "webhook_cleanup": { "attempted": 12, "stopped": 12, "skipped_expired_or_invalid": 0 }, "events_cleanup": { "scope": { "provider": "outlook", "account_id": "jane@acme.com" }, "events_matched": 47, "events_deleted": 47, "bots_cancelled": 6, "schedules_deleted": 6, "events_failed": 0 }, "message": "Outlook connection removed. Account events and scheduled bots cleaned up." } ``` This endpoint is **idempotent** — re-calling it for an already-removed account returns `404 Not Found` without changing state. ### 11b) Disconnect (legacy single-account endpoint) ``` DELETE https://api.meetstream.ai/api/v1/calendar/disconnect ``` Original endpoint, kept for backward compatibility. Behaviour depends on how many connections the MeetStream user has: | Scenario | Body | Behaviour | |---|---|---| | User has **one** connection | `{}` | Tears down that one connection. | | User has **multiple** connections | `{}` | **400 Bad Request** — refuses to disconnect everything implicitly. Use 11a per account or pass an explicit `account_id`. | | Explicit per-account disconnect | `{"provider": "outlook", "account_id": "jane@acme.com"}` | Same effect as the §11a endpoint. | | Provider-scoped disconnect | `{"provider": "outlook"}` | Removes **every** Outlook connection on the user (and only Outlook). Useful when migrating tenants. | | Same as above, keep events | `{"provider": "outlook", "account_id": "jane@acme.com", "purge_events": false}` | Or `?purge_events=false` in the query string. | The 400 response includes a `next_steps` hint so callers know exactly what to do: ```json { "error": "Multiple calendar connections present; refusing to disconnect all without explicit account_id", "scope": "all", "connection_count": 3, "next_steps": "Call DELETE /api/v1/calendar/connections/{provider}/{account_id} per account, or POST this endpoint with body {\"provider\": \"...\", \"account_id\": \"...\"}." } ``` This safety check exists so that one careless API call cannot accidentally wipe out a multi-tenant user's entire calendar history. > Both disconnect paths are irreversible. To reconnect an account, call `POST /calendar/create_outlook_calendar` again with its credentials. --- ## End-to-end setup checklist Here's the full flow to get Outlook Calendar integration running: 1. **Get credentials** — Register an app in Azure Portal, add Microsoft Graph permissions, and run the OAuth helper to get a refresh token for each Microsoft account you want to connect 2. **Connect** — `POST /calendar/create_outlook_calendar` once per account. Each call attaches that account to the same MeetStream user. 3. **Verify** — `GET /calendar/connections` to confirm every account you wanted is attached 4. **Sync** — `GET /calendar/events` to pull in meetings from every connected account (or pass `?provider=outlook&account_id=…` to scope) 5. **Schedule** — `POST /calendar/schedule/{event_id}` to add a bot to a specific meeting 6. **Or auto-schedule** — `POST /calendar/auto-schedule/enable` to cover all meetings across all connected accounts automatically 7. **Recurring** — Set `recurring_event: true` when scheduling, or use `POST /calendar/toggle-recurrence` 8. **Relax** — MeetStream handles calendar changes, rescheduling, cancellations, and subscription renewal per-account from here --- ## API quick reference | Method | Endpoint | Description | |---|---|---| | **Calendar connections** | | | | POST | `/api/v1/calendar/create_outlook_calendar` | Connect one Outlook (or Google) account. Call repeatedly for additional accounts. Pass `{"replace": true}` to rotate tokens on an existing account. | | GET | `/api/v1/calendar/connections` | List every connected account on the user, grouped by provider | | GET | `/api/v1/calendar/connections/{provider}/{account_id}` | Inspect one connection's metadata | | DELETE | `/api/v1/calendar/connections/{provider}/{account_id}` | Disconnect one account (preferred). Optional `?purge_events=false` keeps event history. | | DELETE | `/api/v1/calendar/disconnect` | Legacy disconnect. Returns 400 when the user has >1 connection unless body includes `{provider, account_id}`. | | GET | `/api/v1/calendar/calendars` | List calendars across every connected account (live from Microsoft Graph). `?provider=&account_id=` to scope to one. | | **Events** | | | | GET | `/api/v1/calendar/events` | Sync and list events. Default = every account. `?provider=&account_id=` to scope. | | GET | `/api/v1/calendar/get_events` | List events from local database only (fast). Same `?provider=&account_id=` scoping. | | **Bot scheduling** | | | | POST | `/api/v1/calendar/schedule/{event_id}` | Schedule a bot for an event | | DELETE | `/api/v1/calendar/schedule/{event_id}` | Unschedule a bot for an event | | **Scheduled bot management** | | | | GET | `/api/v1/calendar/scheduled_bots` | List all upcoming scheduled bots | | PATCH | `/api/v1/calendar/scheduled_bots/{bot_id}` | Update a scheduled bot (time, name, attributes) | | DELETE | `/api/v1/calendar/scheduled_bots/{bot_id}` | Delete a specific scheduled bot | | **Auto-scheduling** | | | | POST | `/api/v1/calendar/auto-schedule/enable` | Enable auto-scheduling with default bot config | | POST | `/api/v1/calendar/auto-schedule/disable` | Disable auto-scheduling | | GET | `/api/v1/calendar/auto-schedule/settings` | Get current auto-schedule settings | | **Recurring events** | | | | POST | `/api/v1/calendar/auto-reschedule` | Trigger rescheduling for next recurring occurrence | | POST | `/api/v1/calendar/toggle-recurrence` | Enable/disable auto-rescheduling per event | --- ## FAQ ### Which calendar providers are supported? **Google Calendar** and **Outlook Calendar** (Microsoft 365 / Outlook.com) are both supported on the same MeetStream user. Your calendar can contain meetings from any platform — MeetStream detects Google Meet, Zoom, Microsoft Teams, Webex, GoToMeeting, BlueJeans, and Whereby links. See the [Google Calendar Integration Guide](/guides/app-integrations/using-credentials-with-meetstream-api) for the Google flow. ### Do I need a Microsoft 365 (work/school) account? No. Both personal Microsoft accounts (Outlook.com, Hotmail, Live) and Microsoft 365 work/school accounts are supported. When registering your app in Azure Portal, choose **Accounts in any organizational directory and personal Microsoft accounts** to cover both. You can mix and match: one MeetStream user can connect a personal Outlook.com account and multiple work tenants at the same time. ### Can one MeetStream user connect multiple Microsoft accounts? Yes — up to **200** calendar connections per MeetStream user (Outlook + Google combined). Call `POST /create_outlook_calendar` once per refresh token. Each connection is identified by `(provider="outlook", account_id=)`. Use `GET /calendar/connections` to inspect them, and `DELETE /calendar/connections/{provider}/{account_id}` to remove one without affecting the others. ### What happens if I call `POST /create_outlook_calendar` for the same Microsoft account twice? The second call returns **409 Conflict** with the existing `account_id`. To rotate the refresh token or refresh the scopes for an already-connected account, pass `"replace": true` in the request body — the credentials are updated in place without losing event history or notification subscriptions. ### Can I scope `GET /calendar/events` to just one of my connected accounts? Yes. Pass `?provider=outlook&account_id=`. Without those query parameters, events from every connected account are returned (each row carries `provider` and `account_id` so you can group them client-side). ### How does MeetStream avoid mixing up webhook notifications between my connected accounts? Each connection has its own webhook URL of the form `…/api/v1/admin/schedule/users/{user_id}/{provider}/{account_id}`, and an opaque per-subscription `clientState` issued at registration. Notifications that arrive on the wrong URL or carry a mismatched `clientState` are rejected. Delta tokens, calendar lists, and notification subscriptions are all stored per-account in isolated storage paths. ### I only have one Outlook account connected — do I need to worry about any of this multi-account stuff? No. Every endpoint behaves the same way when you have exactly one connection — you can keep ignoring `provider`, `account_id`, and `/connections`. They become useful the moment you connect a second account. ### How do I get a Microsoft OAuth2 refresh token? Follow **Section 1** of this guide — register an app in Azure Portal, add the required Microsoft Graph permissions, and run the provided OAuth helper (`node server_microsoft.js`) to complete the consent flow. The helper displays your refresh token in the browser. ### How far in advance does auto-scheduling look? The auto-schedule job runs every 24 hours at midnight UTC and schedules bots for meetings happening in the **next 24 hours**. Meetings further out will be picked up in subsequent runs. ### When does the bot join relative to the meeting start time? Bots are scheduled to join **1 minute before** the meeting's start time. ### What happens if I reschedule a meeting in Outlook? MeetStream receives a real-time push notification from Microsoft Graph and automatically updates the bot's scheduled join time to match the new meeting time. Both the EventBridge schedule and the bot record in MeetStream's database are updated, so `GET /scheduled_bots` always shows the correct time. ### What if I cancel a meeting? MeetStream detects the cancellation via the Microsoft Graph notification, deletes the EventBridge schedule, and marks the bot as `Cancelled`. No bot will be created for a cancelled meeting. ### What if a meeting is moved to a time that already passed? MeetStream detects that the new time is in the past, deletes the EventBridge schedule, and cancels the bot record. This prevents a bot from being created for a meeting that can no longer be joined. ### Can I schedule bots for meetings without a meeting link? No. MeetStream requires a valid meeting link (Google Meet, Zoom, Teams, etc.) to join. Events without a detected meeting link are skipped. ### What happens if I schedule a bot for the same event twice? MeetStream deduplicates by event. The second call returns a `409 Conflict` with the existing bot's ID. To update the bot config, use `PATCH /calendar/scheduled_bots/{bot_id}`. ### Can I use different bot configurations for different meetings? Yes. When you manually schedule a bot via `POST /calendar/schedule/{event_id}`, you provide the `bot_config` per event. Auto-scheduling uses your `default_bot_config` for all meetings, but you can override individual events by scheduling them manually. ### Does auto-rescheduling work with all recurrence patterns? MeetStream supports standard iCalendar recurrence rules (RRULE) — daily, weekly, bi-weekly, monthly, yearly, and custom patterns. The next occurrence is calculated from the event's recurrence rule as returned by Microsoft Graph. ### How do I stop a recurring event from being rescheduled? Use the toggle endpoint: `POST /calendar/toggle-recurrence` with `"recurring_enabled": false` for that event. ### Do I need to worry about Microsoft Graph subscriptions expiring? No. MeetStream automatically renews Microsoft Graph change notification subscriptions before they expire. A daily background job checks for subscriptions expiring within 2 days and renews them. Your real-time calendar sync stays active indefinitely without any action on your part. ### What data is deleted when I disconnect my calendar? - **Per-account disconnect (`DELETE /calendar/connections/{provider}/{account_id}`):** Graph subscriptions for that account are stopped, its credentials are wiped from secure storage, the connection record is removed, and (by default) events from that account along with their scheduled bots are deleted. Pass `?purge_events=false` to keep the event history. Other connected accounts are completely untouched. - **Full-user disconnect (`DELETE /calendar/disconnect`):** the legacy endpoint. When only one connection exists, it tears that one down. When multiple connections exist, it refuses (returns 400) unless you specify which account in the body — to prevent accidentally wiping out a multi-tenant user's entire history. Both paths are irreversible. To reconnect, call `POST /calendar/create_outlook_calendar` again. ### Is my Outlook Calendar data stored securely? Yes. OAuth credentials are stored in AWS Systems Manager Parameter Store as encrypted `SecureString` parameters. Event data is stored in DynamoDB with encryption at rest. MeetStream does not store your Microsoft password. ### Can I schedule bots for all occurrences of a recurring event at once? Yes. Pass `"schedule_all_occurrences": true` in the request body when scheduling. You can limit the number of occurrences with `"occurrence_limit"` (default 52). Each occurrence gets its own bot and EventBridge schedule. ### How do I schedule a bot for a specific occurrence of a recurring event? Pass `"occurrence_date": "2026-04-14T10:00:00Z"` in the request body. MeetStream will schedule a bot for that specific occurrence only. ### Can I cancel bots for future occurrences only? Yes. When unscheduling a recurring event, pass `"from_date": "2026-05-01T00:00:00Z"` to cancel only occurrences from that date forward. Occurrences before that date keep their scheduled bots. ### My client secret expired — what do I do? Generate a new client secret in Azure Portal under your app's **Certificates & secrets**, then re-run the OAuth helper to get a fresh refresh token, and call `POST /calendar/create_outlook_calendar` again with the new credentials. MeetStream will replace the old connection automatically. --- For webhook event handling, see the [Webhook Events Guide](MeetStream_Bot_Lifecycle_Webhook_Events_Guide.md). For creating your first bot without calendar integration, see the [First Bot Quickstart](/guides/get-started/create-your-first-bot). For Google Calendar integration, see the [Google Calendar Integration Guide](/guides/app-integrations/using-credentials-with-meetstream-api). # MeetStream Guide: Configure Post‑Call Transcription This guide explains how to enable **post‑call transcription** for your MeetStream bot, choose a provider, and fetch the transcript after the call ends. Supported providers: - `assemblyai` - `deepgram` - `jigsawstack` - `sarvam` - `meetstream` - `meeting_captions` (native captions from Google Meet / Microsoft Teams) ---
--- ## 1) Enable transcription in `recording_config` while creating a bot When calling **Create Bot**, include a `recording_config` object with a `transcript` section. At minimum, you’ll provide: ```json { "recording_config": { "transcript": { "provider": { ... } } } } ``` > You can use **exactly one provider** under `transcript.provider`. --- ## 2) Provider payload examples ### A) JigsawStack (`jigsawstack`) ```json "transcript": { "provider": { "jigsawstack": { "language": "auto", "translate": false, "by_speaker": true } } } ``` --- ### B) Deepgram (`deepgram`) ```json "transcript": { "provider": { "deepgram": { "model": "nova-3", "language": "en", "punctuate": true, "smart_format": true, "diarize": true, "paragraphs": true, "numerals": true, "filler_words": false, "keywords": ["MeetStream", "recording", "transcript"], "utterances": true, "utt_split": 0.8, "detect_language": false, "search": ["MeetStream", "recording"], "tag": ["custom"] } } } ``` --- ### C) AssemblyAI (`assemblyai`) ```json "transcript": { "provider": { "assemblyai": { "speech_models": ["universal-2"], "language_code": "en_us", "speaker_labels": true, "punctuate": true, "format_text": true, "filter_profanity": false, "redact_pii": false, "keyterms_prompt": ["MeetStream"], "auto_chapters": false, "entity_detection": false } } } ``` --- ### D) Sarvam AI (`sarvam`) ```json "transcript": { "provider": { "sarvam": { "model": "saaras:v3", "language_code": "en-IN", "mode": "transcribe", "with_diarization": true } } } ``` --- ### E) MeetStream AI (`meetstream`) ```json "transcript": { "provider": { "meetstream": { "language": "auto", "translate": false } } } ``` --- ### F) Meeting captions (native Google Meet / Teams captions) If you want the **platform’s native captions** (Google Meet / Microsoft Teams), use: ```json "transcript": { "provider": { "meeting_captions": {} } } ``` > For `meeting_captions`, you **do not** use `transcript_id` to fetch captions. > Instead, you’ll fetch the bot details and download the caption file from the returned S3 link. --- ## 3) Getting `transcript_id` after creating the bot When you create a bot with one of these providers: - `assemblyai` - `deepgram` - `jigsawstack` - `sarvam` - `meetstream` The Create Bot response will include a `transcript_id`. Store that `transcript_id` — you’ll use it to retrieve the transcript after the call ends. --- ## 4) Fetch the post‑call transcript using `transcript_id` ### A) Standard transcript (formatted) ```http GET /api/v1/transcript/{{transcript_id}}/get_transcript ``` ### B) Raw transcript ```http GET /api/v1/transcript/{{transcript_id}}/get_transcript?raw=True ``` Replace `{{transcript_id}}` with the value you received in Create Bot response. --- ## 5) Fetch native captions (`meeting_captions` provider) For captions from Google Meet / Teams, you’ll retrieve the caption file using bot details. ### Bot detail endpoint ```http GET /api/v1/bots/{{bot_id}}/detail ``` In this response, you will receive an S3 download link for the caption file (if captions are available). > Captions availability depends on the meeting platform and whether captions were enabled during the meeting. --- ## 6) Recommended workflow (best practice) 1. Create bot with `recording_config.transcript.provider` set. 2. Receive bot lifecycle webhooks (`bot.joining`, `bot.inmeeting`, `bot.stopped`). 3. When you receive `bot.stopped`: - For `assemblyai/deepgram/jigsawstack/sarvam/meetstream`: fetch transcript using `transcript_id`. - For `meeting_captions`: call `/api/v1/bots/{{bot_id}}/detail` and download captions via the S3 link. --- ## Troubleshooting tips - If transcript is not ready immediately after `bot.stopped`, retry after a short delay. - Make sure your provider configuration values (like model/language) are valid for that provider. - If using `meeting_captions`, confirm captions were enabled and supported for the platform. --- # MeetStream Guide: Live Transcription (Streaming) via Webhook This guide explains how to enable **live (real‑time) transcription** for a MeetStream bot using: - `deepgram_streaming` - `assemblyai_streaming` You will receive streaming transcription updates on your webhook URL while the bot is in the meeting. ---
--- ## 1) What you need To use live transcription you must provide: 1) A webhook endpoint that can receive POST requests (public URL). 2) `live_transcription_required.webhook_url` in your create bot payload. 3) A streaming provider config under `recording_config.transcript.provider`. > Tip: For local testing, use **ngrok** or **cloudflared** to expose your local webhook to the internet. --- ## 2) Add `live_transcription_required` to your bot payload In your Create Bot request body: ```json "live_transcription_required": { "webhook_url": "{{webhook_url}}/webhook" } ``` This is the endpoint where MeetStream will POST live transcription events. --- ## 3) Choose a streaming provider (payload examples) ### A) AssemblyAI Streaming (`assemblyai_streaming`) ```json "live_transcription_required": { "webhook_url": "{{webhook_url}}/webhook" }, "recording_config": { "transcript": { "provider": { "assemblyai_streaming": { "transcription_mode": "raw", "sample_rate": 48000, "speech_model": "universal-streaming-english", "format_turns": false, "encoding": "pcm_s16le", "vad_threshold": "0.4", "end_of_turn_confidence_threshold": "0.4", "inactivity_timeout": 300, "min_end_of_turn_silence_when_confident": "400", "max_turn_silence": "1280" } } } } ``` --- ### B) Deepgram Streaming (`deepgram_streaming`) ```json "live_transcription_required": { "webhook_url": "{{webhook_url}}/webhook" }, "recording_config": { "transcript": { "provider": { "deepgram_streaming": { "transcription_mode": "sentence", "model": "nova-2", "language": "en", "punctuate": true, "smart_format": true, "endpointing": 300, "vad_events": true, "utterance_end_ms": 1000, "encoding": "linear16", "channels": 1 } } } } ``` > Use **exactly one** provider in `recording_config.transcript.provider`. --- ## 4) What your webhook will receive (example payload) MeetStream will POST live transcription updates to: `{{webhook_url}}/webhook` Example payload: ```json { "bot_id": "8ceabf49-d392-4c04-8e91-bd9601a0df6e", "speakerName": "Madan Raj", "timestamp": "2026-01-24T17:00:30.354452", "new_text": "hear", "transcript": "hear", "utterance": "", "words": [ { "word": "hear", "start": 2, "end": 2.08, "confidence": 0.999955, "speaker": "Madan Raj", "punctuated_word": "hear", "speech_confidence": 0.999955, "word_is_final": false } ], "end_of_turn": false, "turn_is_formatted": false, "transcription_mode": "word_level", "custom_attributes": { "tag": "Maddy", "sample": "testing" } } ``` ### Field notes (practical) - `bot_id` — use this to map to your session/meeting. - `speakerName` / `speaker` — speaker label (if available). - `new_text` — incremental update (useful for streaming UI). - `transcript` — current transcript buffer (can be partial). - `words[]` — word-level timings + confidence. - `word_is_final` — if `false`, treat as interim (may change). - `end_of_turn` — can be used to “commit” a phrase/segment. - `custom_attributes` — echoed from your Create Bot payload (useful for correlation). --- ## 5) Recommended implementation pattern ### A) Always ACK fast Your webhook should respond **2xx quickly** (e.g., 200 OK) to avoid timeouts. ### B) Store and stream Common approaches: - **Live UI captions:** append `new_text` when `word_is_final=true`, or only commit when `end_of_turn=true`. - **Analytics / downstream triggers:** run actions when `end_of_turn=true` (less noisy). - **Persist to DB:** store word chunks with timestamps, or store turn-level segments. ### C) De-dup / idempotency Webhooks can occasionally arrive close together. Consider de-duping with: - `{bot_id, timestamp, new_text}` or - a rolling hash of the payload body. --- ## 6) Troubleshooting - No live transcription arriving? - Confirm `live_transcription_required.webhook_url` is reachable publicly (try `curl`). - Ensure your webhook endpoint path is correct (`/webhook`). - Confirm your webhook server accepts **POST** and returns **2xx**. - Getting payloads but UI looks “jumpy”? - Treat `word_is_final=false` as interim. - Only commit text when `end_of_turn=true` or `word_is_final=true`. --- # MeetStream Guide: Per-Participant Video Streams This guide explains how to capture **individual video recordings per participant** in a meeting. Instead of a single composite screen recording, you receive a separate WebM video file for each participant's webcam and any screen shares. Supported platforms: **Google Meet, Zoom, Microsoft Teams**. --- ## 1) What you need 1. A MeetStream API key (see the [Quickstart Guide](/guides/get-started/create-your-first-bot)). 2. A meeting link (Google Meet, Zoom, or Microsoft Teams). 3. Set `video_separate_streams: true` in your Create Bot request. --- ## 2) Create a bot with per-participant video Use the [Create Bot Endpoint](https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/create-bot) ### Minimal example ```bash curl -X POST "https://api.meetstream.ai/api/v1/bots/create_agent" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "meeting_link": "", "bot_name": "RecorderBot", "video_separate_streams": true }' ``` ### Full example with recording config ```json { "meeting_link": "https://meet.google.com/abc-defg-hij", "bot_name": "RecorderBot", "video_required": true, "video_separate_streams": true, "recording_config": { "transcript": { "provider": { "deepgram": { "model": "nova-3", "language": "en" } } } }, "automatic_leave": { "everyone_left_timeout": 5 } } ``` ### Parameter reference | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `video_separate_streams` | boolean | `false` | Enable per-participant video capture | | `video_required` | boolean | `false` | Enable composite screen recording (single MP4). Can be used alongside `video_separate_streams` | > **Tip:** You can also pass `video_separate_streams` nested inside `recording_config`: > ```json > "recording_config": { > "video_separate_streams": true > } > ``` > The top-level parameter takes precedence if both are set. --- ## 3) What happens during the meeting Once the bot joins, it automatically: - Captures each participant's **webcam feed** as a separate video stream. - Captures **screen shares** as separate streams. - Records up to **6 concurrent webcam streams**. If more than 6 participants have cameras on, additional cameras are queued and start recording when a slot frees up. - Screen shares are always captured (1 concurrent on Teams/Zoom, multiple possible on Google Meet). No additional configuration is needed per participant -- the bot handles everything. --- ## 4) Retrieve per-participant video streams After the bot leaves and video processing completes, call: ```bash curl -X GET "https://api.meetstream.ai/api/v1/bots//get_recording_streams" \ -H "Authorization: Token " ``` ### Response when processing is complete ```json { "bot_id": "f923cd21-da86-4d77-b37b-0707d37751c8", "video_status": "Success", "recording_streams_available": true, "participants": [ { "participant_name": "John Doe", "streams": [ { "stream_id": "user_456", "segments": [ { "segment_index": 0, "url": "https://s3.amazonaws.com/...?X-Amz-Signature=...", "filename": "John_Doe_456_0.webm", "duration_seconds": 125.5, "width": 640, "height": 360, "codec": "vp8" } ] } ] }, { "participant_name": "Jane Smith", "streams": [ { "stream_id": "user_789", "segments": [ { "segment_index": 0, "url": "https://s3.amazonaws.com/...?X-Amz-Signature=...", "filename": "Jane_Smith_789_0.webm", "duration_seconds": 130.2, "width": 854, "height": 480, "codec": "vp8" } ] } ] } ], "screenshares": [ { "participant_name": "John Doe", "streams": [ { "stream_id": "source_101", "segments": [ { "segment_index": 0, "url": "https://s3.amazonaws.com/...?X-Amz-Signature=...", "filename": "John_Doe_101_0.webm", "duration_seconds": 60.0, "width": 1280, "height": 720, "codec": "vp8" } ] } ] } ], "summary": { "total_participants": 2, "total_screenshares": 1, "total_segments": 3 } } ``` ### Response when processing is still in progress ```json { "video_status": "processing", "stage": "Core Processing", "message": "Video is being processed. Try again later." } ``` HTTP status **202** is returned when processing is not yet complete. The `stage` field indicates the current processing step. Poll again after 10-30 seconds. --- ## 5) Download the video files Each segment in the response contains a `url` field -- a **presigned S3 URL** that allows direct download without additional authentication. ```bash # Download a participant's video curl -o "John_Doe.webm" "" ``` > **Important:** Presigned URLs expire after **10 minutes**. If a URL has expired, call `get_recording_streams` again to get fresh URLs. ### File format | Property | Value | |----------|-------| | Container | WebM | | Video codec | VP8 | | Frame rate | 15 FPS | | Audio | None (video-only files) | Audio is available separately via the standard **Get Bot Audio** endpoint. --- ## 6) Understanding segments A participant may have **multiple segments**. A new segment is created when: - A **screen share resolution changes** (e.g., switching from fullscreen 1080p to a smaller window) on Teams or Zoom. - A participant **shares their screen multiple times** (each share session is a separate segment). - A **webcam video dimension change** occurs on Teams. Camera off/on does **not** create a new segment -- the bot maintains a continuous recording file per participant across camera toggles. Segments are ordered by `segment_index` and are chronologically sequential. Use the `duration_seconds` field to determine segment timing. --- ## 7) Video resolution by platform Webcam and screen share resolutions differ by platform: | Platform | Webcam | Webcam Bitrate | Screen share | Screen share Bitrate | |----------|--------|---------------|-------------|---------------------| | Google Meet | Up to 854x480 | 800 kbps | Up to 1280x720 | 2 Mbps | | Microsoft Teams | Up to 854x480 | 800 kbps | Up to 1280x720 | 2 Mbps | | Zoom | 640x360 (fixed) | 300 kbps | Native resolution | 1.5 Mbps | --- ## 8) Using both composite and per-participant video You can enable both `video_required` and `video_separate_streams` simultaneously: ```json { "meeting_link": "", "video_required": true, "video_separate_streams": true } ``` This produces: - A **composite MP4** (single screen recording of the meeting view) -- retrieve via the **Get Bot Video** endpoint. - **Per-participant WebM files** -- retrieve via the **Get Recording Streams** endpoint. --- ## 9) Webhook notifications If you have webhooks configured, you will receive notifications when video processing starts and completes. Use the `video_status` field in webhook payloads or poll `get_recording_streams` until `video_status` is `"Success"`. --- ## 10) Troubleshooting - **`recording_streams_available: false` with `video_status: "Success"`?** - The bot may have been in the meeting but no participant had their camera on. Per-participant streams are only produced for participants whose cameras were active. - **Missing a participant's video?** - The bot captures up to **6 concurrent webcam streams**. If more than 6 cameras were active, some participants may not have been captured. - Participants who never turned on their camera will not appear in the response. - **Getting `video_status: "processing"`?** - Video processing takes a short time after the bot exits. Poll again after 10-30 seconds. - **Presigned URL returns 403 Forbidden?** - The URL has expired (10-minute lifetime). Call `get_recording_streams` again for fresh URLs. - **Video file has no audio?** - Per-participant video files are **video-only**. Use the **Get Bot Audio** endpoint to retrieve the meeting audio separately. # MeetStream Guide: Per-Participant Audio Streams This guide explains how to capture **individual audio recordings per participant** in a meeting. Instead of a single mixed audio track, you receive a separate WebM audio file for each speaker. Supported platforms: **Google Meet, Zoom**. --- ## 1) What you need 1. A MeetStream API key (refer to [Dashboard Setup](/guides/get-started/dashboard-setup)). 2. A meeting link (Google Meet or Zoom). 3. Set `audio_separate_streams: true` in your Create Bot request. --- ## 2) Create a bot with per-participant audio Use the **Create Bot** endpoint: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/create-bot ### Minimal example ```bash curl -X POST "https://api.meetstream.ai/api/v1/bots/create_bot" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "meeting_link": "", "bot_name": "RecorderBot", "audio_separate_streams": true }' ``` ### Full example with recording config ```json { "meeting_link": "https://meet.google.com/abc-defg-hij", "bot_name": "RecorderBot", "audio_separate_streams": true, "recording_config": { "transcript": { "provider": { "deepgram": { "model": "nova-3", "language": "en" } } } }, "automatic_leave": { "everyone_left_timeout": 5 } } ``` ### Parameter reference | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `audio_separate_streams` | boolean | `false` | Enable per-participant audio capture | > **Tip:** You can also pass `audio_separate_streams` nested inside `recording_config`: > ```json > "recording_config": { > "audio_separate_streams": true > } > ``` > The top-level parameter takes precedence if both are set. --- ## 3) What happens during the meeting Once the bot joins, it automatically: - Captures each participant's audio as a **separate file**. - Records up to **16 concurrent speaker streams**. - Continues recording throughout the meeting -- no configuration needed per participant. The bot also continues to produce the standard **mixed audio** file (`audio.wav`) regardless of whether `audio_separate_streams` is enabled. > **Note on platform behaviour:** The mechanism for capturing per-participant audio differs between Zoom and Google Meet and affects the isolation level of each file. See [Section 7](#7-per-participant-audio-by-platform) for details. --- ## 4) Retrieve per-participant audio streams After the bot leaves and audio processing completes, call: ```bash curl -X GET "https://api.meetstream.ai/api/v1/bots//get_audio_streams" \ -H "Authorization: Token " ``` ### Response when processing is complete ```json { "bot_id": "f923cd21-da86-4d77-b37b-0707d37751c8", "audio_status": "Success", "audio_streams_available": true, "participants": [ { "participant_name": "John Doe", "streams": [ { "stream_id": "JohnDoe_abc123", "segments": [ { "segment_index": 0, "url": "https://s3.amazonaws.com/...?X-Amz-Signature=...", "filename": "John_Doe_abc123_0.webm", "duration_seconds": 125.5, "sample_rate": 48000, "channels": 1, "codec": "opus" } ] } ] }, { "participant_name": "Jane Smith", "streams": [ { "stream_id": "JaneSmith_def456", "segments": [ { "segment_index": 0, "url": "https://s3.amazonaws.com/...?X-Amz-Signature=...", "filename": "Jane_Smith_def456_0.webm", "duration_seconds": 118.3, "sample_rate": 48000, "channels": 1, "codec": "opus" } ] } ] } ], "summary": { "total_participants": 2, "total_segments": 2 } } ``` ### Response when processing is still in progress ```json { "audio_status": "in_progress", "message": "Bot is still in the meeting. Audio streams will be available after the bot leaves." } ``` HTTP status **202** is returned when the bot has not yet left the meeting. Poll again after the bot exits. ### Response when no audio streams are available ```json { "bot_id": "f923cd21-da86-4d77-b37b-0707d37751c8", "audio_status": "Success", "audio_streams_available": false, "message": "No per-participant audio streams available for this bot.", "participants": [] } ``` This is returned when `audio_separate_streams` was not enabled, or the meeting ended before any speech was detected. --- ## 5) Download the audio files Each segment in the response contains a `url` field -- a **presigned S3 URL** that allows direct download without additional authentication. ```bash # Download a participant's audio curl -o "John_Doe.webm" "" ``` > **Important:** Presigned URLs expire after **10 minutes**. If a URL has expired, call `get_audio_streams` again to get fresh URLs. ### File format | Property | Value | |----------|-------| | Container | WebM | | Audio codec | Opus | | Bitrate | 48 kbps | | Sample rate | 48,000 Hz | | Channels | Mono | --- ## 6) Understanding segments Each participant has one segment (`segment_index: 0`) covering the full duration of their speech in the meeting. Silence gaps between speech periods are preserved with actual silence, so the file's timeline aligns with the real meeting timeline. Use `duration_seconds` to determine how long a participant was speaking. --- ## 7) Per-participant audio by platform The quality of speaker isolation differs by platform due to how each platform delivers audio. | Platform | Isolation | Mechanism | |----------|-----------|-----------| | **Zoom** | Full isolation | Zoom SDK provides a dedicated raw PCM stream per participant. Each file contains only that participant's microphone audio. | | **Google Meet** | Partial isolation | Up to 3 concurrent speaker streams are captured via WebRTC CSRC demuxing. Audio is attributed to whoever is speaking at each moment. | > **Zoom** produces the cleanest per-participant files. **Google Meet** files contain the meeting's mixed audio during the periods when that participant was the active speaker. --- ## 8) Using both mixed and per-participant audio Per-participant audio and the standard mixed audio are always captured simultaneously. You do not need to choose one or the other. ```json { "meeting_link": "", "audio_separate_streams": true } ``` This produces: - A **mixed audio WAV** (all participants combined) -- retrieve via the **Get Bot Audio** endpoint (`/api/v1/bots//get_audio`). - **Per-participant WebM files** -- retrieve via the **Get Audio Streams** endpoint (`/api/v1/bots//get_audio_streams`). --- ## 9) Using per-participant audio with per-participant video Both flags can be enabled together: ```json { "meeting_link": "", "audio_separate_streams": true, "video_separate_streams": true } ``` This produces separate audio and video files per participant. The files are not muxed together -- audio and video are delivered as independent files. Match them by `participant_name` across the two API responses. --- ## 10) Webhook notifications If you have webhooks configured, you will receive an `audio.processed` event when audio processing (including per-participant stream generation) completes. Poll `get_audio_streams` until `audio_streams_available` is `true` if you prefer polling over webhooks. --- ## 11) Troubleshooting - **`audio_streams_available: false` with `audio_status: "Success"`?** - Confirm that `audio_separate_streams: true` was set when the bot was created. - The meeting may have ended before any speech was detected. - **Missing a participant's audio?** - The bot captures up to **16 concurrent speaker streams**. Meetings with more than 16 active speakers may result in some participants not being captured. - Participants who never spoke will not appear in the response. - **Getting status `in_progress`?** - The bot is still in the meeting. Audio streams are generated after the bot exits. Wait for the bot to leave and poll again. - **Presigned URL returns 403 Forbidden?** - The URL has expired (10-minute lifetime). Call `get_audio_streams` again for fresh URLs. - **Audio file sounds like the whole meeting, not just one person?** - This is expected on **Google Meet** -- see [Section 7](#7-per-participant-audio-by-platform). The file contains the meeting audio during that participant's active speaking windows. On **Zoom**, files are fully isolated. # MeetStream Guide: Webhook Events This guide explains how to receive **webhook events** from MeetStream, what each event means, and how to trigger follow-up actions like fetching **audio, video, or transcription**. Applies to: **Google Meet, Zoom, Microsoft Teams**. Support: docs.meetstream.ai • API: api.meetstream.ai --- ## 1) Add your webhook URL while creating the bot When you create a bot, include your webhook endpoint in the payload as: ```json { "callback_url": "{{webhook_url}}" } ``` MeetStream will send HTTP POST requests to your `callback_url` whenever the bot's status changes during its lifecycle and when post-call processing completes. ### Example (Create Bot + callback_url) Docs: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/create-bot ```bash curl -X POST "https://api.meetstream.ai/api/v1/bots/create_bot" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "meeting_link": "", "video_required": false, "callback_url": "https://your-domain.com/webhooks/meetstream" }' ``` > Tip: Your webhook should respond quickly with **2xx** (e.g., 200) to acknowledge receipt. --- ## 2) Webhook payload format Every webhook notification follows this structure: ```json { "bot_event": "bot.joining", "bot_id": "6667fd0c-0165-471a-a880-06a1180be377", "bot_status": "Joining", "message": "Bot is joining the meeting", "status_code": 200, "timestamp": "2026-02-27T07:11:51.863543+00:00", "custom_attributes": {} } ``` ### Fields - `bot_event` — the event name. Identifies what just happened (`bot.joining`, `bot.in_waiting_room`, `bot.inmeeting`, `bot.recording`, `bot.leaving`, `bot.stopped`, `bot.kicked`, `bot.denied`, `bot.notallowed`, `bot.failed`, `bot.done`, `audio.processed`, `transcription.processed`, `video.processed`, `data_deletion`, etc.). **Branch on this field.** - `bot_id` — unique identifier for the bot session - `bot_status` — status detail (see **Status Reference**) - `message` — human readable explanation - `status_code` — `200` for success stages; `500` for any failure (`Error`, `Denied`, `NotAllowed`) - `timestamp` — ISO-8601 timestamp - `custom_attributes` — echoed back if you provided it when creating the bot --- ## 3) Event types (what you'll receive) MeetStream sends two categories of webhook events: **bot lifecycle events** and **post-call processing events**. ### A) Bot lifecycle events These events track the bot's full lifecycle, from join to post-call completion: | `bot_event` | What it means | |---|---| | `bot.scheduled` | (Scheduled bots only — when `join_at` is set) MeetStream accepted the scheduling request and created the schedule. Fires immediately after `POST /bots`. Confirms the schedule was set; the bot will not actually join until the `scheduled_join_time` carried on the payload. | | `bot.joining` | MeetStream accepted the bot creation request and dispatched the bot (fired by the API server, **not** the bot container). For scheduled bots, fires when the scheduled execution time triggers. | | `bot.in_waiting_room` | Bot has clicked Join and is now waiting to be admitted. Fires for **every** bot once between `bot.joining` and `bot.inmeeting` — briefly for instant-admit meetings; lingers if the host has a lobby enabled. | | `bot.inmeeting` | Bot successfully joined and is actively in the meeting | | `bot.recording` | Bot started capturing audio/video (fires once after recording actually begins) | | `bot.recording_permission_allowed` | **(Zoom only)** Host granted the bot recording permission. Fires between `bot.inmeeting` and `bot.recording`. | | `bot.recording_permission_denied` | **(Zoom only)** Host explicitly denied recording permission OR did not respond within the timeout. Followed by `bot.leaving` → `bot.stopped`. | | `bot.leaving` | Bot is exiting the meeting (transitional event before any terminal event) | | `bot.stopped` | Clean exit — meeting ended, API stop, host ended, or any other graceful terminal cause. `bot_status: "Stopped"`, `status_code: 200`. | | `bot.kicked` | Bot was forcibly removed from the meeting by the host or a participant. `bot_status: "Stopped"`, `status_code: 200`. | | `bot.denied` | Host explicitly denied the bot's join request. `bot_status: "Denied"`, `status_code: 500`. (For Zoom recording-permission denial, see `bot.recording_permission_denied`.) | | `bot.notallowed` | Bot was not admitted (waiting-room/lobby timeout). `bot_status: "NotAllowed"`, `status_code: 500`. | | `bot.failed` | Unexpected error during the bot's lifecycle. `bot_status: "Error"`, `status_code: 500`. | | `bot.done` | Post-call pipeline finished (transcript ready or skipped) | #### Example payloads **`bot.scheduled`** *(scheduled bots only — fires immediately after `POST /bots` when `join_at` is set)* ```json { "bot_event": "bot.scheduled", "bot_id": "6667fd0c-0165-471a-a880-06a1180be377", "bot_status": "Scheduled", "message": "Bot scheduled to join at 2026-05-18T08:10:00+00:00", "status_code": 200, "timestamp": "2026-05-17T19:42:13.000000+00:00", "scheduled_join_time": "2026-05-18T08:10:00+00:00", "custom_attributes": {} } ``` > **About `bot.scheduled`:** Confirms to webhook-driven integrations that the schedule was accepted. The bot will not actually join the meeting until `scheduled_join_time` — at which point `bot.joining` and the normal lifecycle fire. Without subscribing to this event, the only signal that a scheduled bot was created is the 201 response body (or polling `GET /bots/{id}` and checking `Status: "Scheduled"`). **`bot.joining`** *(fired by the API server when the bot is dispatched)* ```json { "bot_event": "bot.joining", "bot_id": "6667fd0c-0165-471a-a880-06a1180be377", "bot_status": "Joining", "message": "Bot is joining the meeting", "status_code": 200, "timestamp": "2026-05-18T08:10:00.000000+00:00", "custom_attributes": {} } ``` **`bot.in_waiting_room`** ```json { "bot_event": "bot.in_waiting_room", "bot_id": "6667fd0c-0165-471a-a880-06a1180be377", "bot_status": "InWaitingRoom", "message": "Bot is waiting to be admitted", "status_code": 200, "timestamp": "2026-05-18T08:10:05.000000+00:00", "custom_attributes": {} } ``` **`bot.inmeeting`** ```json { "bot_event": "bot.inmeeting", "bot_id": "6667fd0c-0165-471a-a880-06a1180be377", "bot_status": "InMeeting", "message": "Bot successfully joined the meeting", "status_code": 200, "timestamp": "2026-05-18T08:10:12.000000+00:00", "custom_attributes": {} } ``` **`bot.recording`** ```json { "bot_event": "bot.recording", "bot_id": "6667fd0c-0165-471a-a880-06a1180be377", "bot_status": "Recording", "message": "Bot started recording", "status_code": 200, "timestamp": "2026-05-18T08:10:13.000000+00:00", "custom_attributes": {} } ``` **`bot.recording_permission_allowed`** *(Zoom only)* ```json { "bot_event": "bot.recording_permission_allowed", "bot_id": "6667fd0c-0165-471a-a880-06a1180be377", "bot_status": "RecordingPermissionAllowed", "message": "Recording permission granted by host", "status_code": 200, "timestamp": "2026-05-18T08:10:12.500000+00:00", "custom_attributes": {} } ``` **`bot.recording_permission_denied` — denied by host** *(Zoom only)* ```json { "bot_event": "bot.recording_permission_denied", "bot_id": "6667fd0c-0165-471a-a880-06a1180be377", "bot_status": "RecordingPermissionDenied", "message": "Failed: Recording permission denied by host", "status_code": 500, "timestamp": "2026-05-18T08:11:00.000000+00:00", "custom_attributes": {} } ``` **`bot.recording_permission_denied` — host did not respond in time** *(Zoom only)* ```json { "bot_event": "bot.recording_permission_denied", "bot_id": "6667fd0c-0165-471a-a880-06a1180be377", "bot_status": "RecordingPermissionDenied", "message": "Failed: Recording permission timeout", "status_code": 500, "timestamp": "2026-05-18T08:11:12.000000+00:00", "custom_attributes": {} } ``` **`bot.leaving`** ```json { "bot_event": "bot.leaving", "bot_id": "6667fd0c-0165-471a-a880-06a1180be377", "bot_status": "Leaving", "message": "Bot is leaving the meeting", "status_code": 200, "timestamp": "2026-05-18T09:05:40.000000+00:00", "custom_attributes": {} } ``` ### Terminal-event payloads Every terminal scenario fires a **single** webhook. Branch on `bot_event` to distinguish the cause. **Clean exit (`bot.stopped`)** ```json { "bot_event": "bot.stopped", "bot_id": "6667fd0c-0165-471a-a880-06a1180be377", "bot_status": "Stopped", "message": "Bot exited the call: Meeting ended by host", "status_code": 200, "timestamp": "2026-05-18T09:05:41.000000+00:00", "custom_attributes": {} } ``` **Kicked by host/participant (`bot.kicked`)** ```json { "bot_event": "bot.kicked", "bot_id": "6667fd0c-0165-471a-a880-06a1180be377", "bot_status": "Stopped", "message": "Bot was removed from the meeting by a participant", "status_code": 200, "timestamp": "2026-05-18T08:45:00.000000+00:00", "custom_attributes": {} } ``` **Lobby timeout (`bot.notallowed`)** ```json { "bot_event": "bot.notallowed", "bot_id": "6667fd0c-0165-471a-a880-06a1180be377", "bot_status": "NotAllowed", "message": "Failed: Not admitted to meeting", "status_code": 500, "timestamp": "2026-05-18T08:15:00.000000+00:00", "custom_attributes": {} } ``` **Host denied join (`bot.denied`)** ```json { "bot_event": "bot.denied", "bot_id": "6667fd0c-0165-471a-a880-06a1180be377", "bot_status": "Denied", "message": "Failed: Host denied join", "status_code": 500, "timestamp": "2026-05-18T08:20:00.000000+00:00", "custom_attributes": {} } ``` > **Note:** For **Zoom** recording-permission denial, the bot emits `bot.recording_permission_denied` followed by `bot.leaving` → `bot.stopped` — **not** `bot.denied`. See the `bot.recording_permission_denied` example above. **Unexpected failure (`bot.failed`)** ```json { "bot_event": "bot.failed", "bot_id": "6667fd0c-0165-471a-a880-06a1180be377", "bot_status": "Error", "message": "Error: Failed to connect to meeting (stuck in connecting state)", "status_code": 500, "timestamp": "2026-05-18T08:11:00.000000+00:00", "custom_attributes": {} } ``` > **`bot.done`** is the terminal event of the post-call pipeline — see Section 3.B below for the full payload, behavior, and its relationship with `bot_status: "Done"`. --- **Important notes:** - Every bot starts with `bot.joining`, fired by the MeetStream API server the moment the bot creation request is accepted and dispatched. For scheduled bots (`join_at`), the API server **also** fires `bot.scheduled` immediately on `POST /bots` to confirm the schedule was accepted; `bot.joining` then fires later when the scheduled execution time triggers. - `bot.in_waiting_room` is fired for **every** bot once the container clicks Join. For meetings without a lobby it fires briefly before `bot.inmeeting`; for meetings with a host-admit lobby it lingers until the host admits. - If the bot joins successfully, you will receive `bot.inmeeting`. - `bot.recording` is sent once the bot actually starts capturing audio/video. For Zoom this is gated on host-granted recording permission, so there can be a noticeable gap between `bot.inmeeting` and `bot.recording`. For Teams/GMeet, `bot.recording` typically fires within a second of `bot.inmeeting`. - **(Zoom only)** Between `bot.inmeeting` and `bot.recording`, you may receive `bot.recording_permission_allowed` once the host grants recording permission. If the host denies the request OR does not respond within the configured timeout, you'll receive `bot.recording_permission_denied`, followed by the usual `bot.leaving` → `bot.stopped` terminal sequence (note: the terminal is `bot.stopped`, **not** `bot.denied` — recording-permission denial ends with a clean stop). - `bot.leaving` is emitted briefly before any terminal event (whether normal exit, kick, denial, or failure). - The in-meeting lifecycle ends with **exactly one** terminal webhook: `bot.stopped` / `bot.kicked` / `bot.denied` / `bot.notallowed` / `bot.failed`. - If the bot **fails to join** (lobby timeout/denied/error), you may get `bot.leaving` then a failure terminal event right after `bot.joining` **without** a `bot.inmeeting`. - After the in-meeting terminal event, MeetStream runs the post-call pipeline (audio upload, video upload, transcription) and you'll receive the artifact-specific events (`audio.processed`, `video.processed`, `transcription.processed`) followed by `bot.done` when the pipeline completes. - Failure terminals (`bot.denied`, `bot.notallowed`, `bot.failed`) carry `status_code: 500` and `message` prefixed with `"Error: "` or `"Failed: "` (see Section 4). Clean terminals (`bot.stopped`, `bot.kicked`) carry `status_code: 200`. ### B) Post-call processing events After the in-meeting terminal webhook, MeetStream continues processing the recorded session in the background. You will receive additional webhook events as each artifact finishes processing, plus a terminal `bot.done` once the pipeline is complete and `data_deletion` if/when the media is removed: | `bot_event` | What it means | `bot_status` after the event | |---|---|---| | `audio.processed` | Audio extraction and processing completed | `MediaProcessing` | | `transcription.processed` | Transcription generation completed | `MediaProcessing` | | `video.processed` | Video processing completed | `MediaProcessing` | | `bot.done` | Post-call pipeline finished — audio/video/transcription artifacts are ready to fetch | `Done` | | `data_deletion` | Bot media has been deleted (manually via `DELETE /bots/{id}` or automatically when the retention window elapses) | `MediaExpired` | These events arrive **asynchronously** after the bot has stopped — the order and timing depend on processing duration for each artifact. The expected order on a healthy bot is roughly: ``` audio.processed transcription.processed // (any order vs audio/video) video.processed bot.done // pipeline complete ... (later, on retention or manual delete) data_deletion // media gone ``` > **`Status` field progression during post-call:** the live `Status` column on the bot record (visible via `GET /bots/{id}`) advances through `MediaProcessing` (pipeline started) → `Done` (pipeline finished, matches the `bot.done` webhook) → `MediaExpired` (media deleted, matches `data_deletion`). `MediaProcessing` is a status-only transition and does not have its own webhook — track the artifact-specific events instead. #### Example payloads **`audio.processed`** ```json { "bot_id": "5b0ff6e7-3cea-4c9f-a6b4-851c5f11cf4f", "bot_event": "audio.processed", "audio_status": "Success", "message": "Audio processing completed successfully", "status_code": 200 } ``` **`transcription.processed`** ```json { "bot_id": "5b0ff6e7-3cea-4c9f-a6b4-851c5f11cf4f", "bot_event": "transcription.processed", "transcript_status": "Success", "message": "Transcript processing completed successfully", "status_code": 200 } ``` **`video.processed`** ```json { "bot_id": "5b0ff6e7-3cea-4c9f-a6b4-851c5f11cf4f", "bot_event": "video.processed", "video_status": "Success", "message": "Video processing completed successfully", "status_code": 200 } ``` **`bot.done`** — post-call pipeline finished ```json { "bot_event": "bot.done", "bot_id": "6667fd0c-0165-471a-a880-06a1180be377", "bot_status": "Done", "message": "Post-call processing completed", "status_code": 200, "timestamp": "2026-05-18T09:07:30.000000+00:00", "custom_attributes": {} } ``` > **About `bot.done`:** Always carries `status_code: 200` — it signals only that the post-call pipeline has *finished*, not whether each step succeeded. Inspect the artifact-specific events above (`audio.processed`, `transcription.processed`, `video.processed`, `transcription.failed`, etc.) for per-step success or failure. **`data_deletion`** — media has been deleted (matches `Status=MediaExpired`) ```json { "bot_id": "5b0ff6e7-3cea-4c9f-a6b4-851c5f11cf4f", "bot_event": "data_deletion", "status": "success", "message": "Bot data deleted successfully", "deleted_objects": 5, "timestamp": "2024-01-15T14:30:00Z", "status_code": 200 } ``` > **About `data_deletion`:** Fires once when the bot's media is removed from MeetStream's storage — either when the customer calls `DELETE /api/v1/bots/{bot_id}` or when the retention window configured at bot-creation time elapses. After this event the bot's `Status` column reads `MediaExpired` and the artifact-fetch endpoints (`GET /bots/{id}/audio`, `/video`, `/transcript`) will return 404/410. --- ## 4) Status reference (bot_status) `bot_status` gives more detail. The table below shows which `bot_status` value each event carries, alongside the `bot_event` name. Note that `bot_status` for `bot.kicked` is reported as `"Stopped"` (not `"Kicked"`) — branch on `bot_event` to distinguish kicks from clean stops. | bot_status (wire) | `bot_event` | Meaning | |---|---|---| | `Scheduled` | `bot.scheduled` | (Scheduled bots only) Schedule accepted. Bot will join at `scheduled_join_time` on the payload. | | `Joining` | `bot.joining` | Bot creation request accepted; bot has been dispatched (fired by the API server, not the container) | | `InWaitingRoom` | `bot.in_waiting_room` | Bot has clicked Join and is now waiting to be admitted (instant for most meetings; lingers if host has a lobby enabled). Fires for every bot. | | `InMeeting` | `bot.inmeeting` | Bot is in the meeting (admitted) | | `Recording` | `bot.recording` | Bot started capturing audio/video. For Zoom this fires after the host grants recording permission; for Teams/GMeet it fires on the first audio frame received | | `RecordingPermissionAllowed` | `bot.recording_permission_allowed` | **(Zoom only)** Host granted recording permission. Fires between `InMeeting` and `Recording` | | `RecordingPermissionDenied` | `bot.recording_permission_denied` | **(Zoom only)** Host denied recording permission or did not respond within the timeout. Followed by `Leaving` → `Stopped` | | `Leaving` | `bot.leaving` | Bot is exiting the meeting (transitional, fires before any terminal event) | | `Stopped` | `bot.stopped` | Clean exit — meeting ended, API stop, host ended, or any other graceful terminal cause. `status_code: 200`. | | `Stopped` | `bot.kicked` | Bot was forcibly removed from the meeting by the host or a participant. Note `bot_status` is `"Stopped"`, not `"Kicked"` — branch on `bot_event` to detect kicks. `status_code: 200`. | | `NotAllowed` | `bot.notallowed` | Bot could not join (commonly waiting room/lobby timeout). `status_code: 500`. | | `Denied` | `bot.denied` | Host explicitly denied the bot's join request (for Zoom recording-permission denial, the terminal is the clean `bot.stopped`). `status_code: 500`. | | `Error` | `bot.failed` | Unexpected error during lifecycle. `status_code: 500`. | | `MediaProcessing` | *(no webhook)* | Bot has exited the meeting and the post-call pipeline has begun (audio/video upload, transcription). Visible via GET `/bots/{id}` but does not fire its own webhook — track artifact-specific events (`audio.processed`, `transcription.processed`, `video.processed`) for progress. | | `Done` | `bot.done` | Post-call pipeline finished. The live `Status` column also flips to `Done` at this moment (matches the `bot.done` webhook). | | `MediaExpired` | `data_deletion` | Bot media has been deleted — either manually via `DELETE /bots/{id}` or automatically when the configured retention window elapses. The `data_deletion` webhook carries the matching details. | ### Failure messages When a stage fails, `status_code` is `500` and `message` carries a prefix you can grep on for alerting: - `"Error:
"` — unexpected exceptions (uncaught errors, AWS API failures) - `"Failed:
"` — handled/expected failures (validation, timeout, denial) Examples: - `"Error: Failed to start recording"` - `"Failed: Recording permission denied by host"` - `"Failed: Not admitted to meeting"` - `"Error: Transcript processing failed"` --- ## 5) Recommended actions based on webhook events Your server can react to webhook events and run follow-up workflows. ### A) On `bot.inmeeting` Typical actions: - Update your UI/state: "bot is live" - Start timers / internal tracking - Notify other systems that recording has started ### B) On any terminal event (`bot.stopped` / `bot.kicked` / `bot.denied` / `bot.notallowed` / `bot.failed`) This is the moment to mark the session as complete. Artifacts may still be processing at this point. Recommended flow: 1. Receive a terminal webhook 2. Branch on `bot_event`: - `bot.stopped` — clean exit (meeting ended, API stop, host ended, etc.). Wait for post-call processing events before fetching outputs. - `bot.kicked` — bot was forcibly removed by host/participant. Recording up to that point is still processed. - `bot.denied` — host explicitly denied join. Log the `message` and alert/handle accordingly. - `bot.notallowed` — bot was not admitted (lobby/waiting-room timeout). Log and handle accordingly. - `bot.failed` — unexpected error during the bot's lifecycle. Log `message` and alert. ### C) On post-call processing events Once you receive `audio.processed`, `transcription.processed`, or `video.processed`, the corresponding artifact is ready to fetch: - Audio: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/get-bot-audio - Video: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/get-bot-video ### D) On `data_deletion` Confirms that bot data has been removed. Update your records accordingly — further fetch requests for this bot's artifacts will fail. --- ## 6) Webhook signing (optional but recommended) If you configure a webhook secret, MeetStream includes an HMAC signature in headers: - `X-MeetStream-Signature`: `sha256=` - `X-MeetStream-Timestamp`: ISO 8601 timestamp Verification steps: 1. Compute `HMAC-SHA256(your_secret, raw_request_body)` 2. Compare with `X-MeetStream-Signature` (strip `sha256=` prefix) 3. Optionally validate timestamp is within an acceptable window (replay protection) --- ## 7) Retry behavior (important) - Webhook delivery is **best-effort**. - If your endpoint returns a **non-2xx**, the webhook is **not retried**. - A bot may send up to **3 `bot.joining`** events if join retries are configured. - `bot.inmeeting` is sent **at most once**. Exactly **one** terminal webhook (`bot.stopped` / `bot.kicked` / `bot.denied` / `bot.notallowed` / `bot.failed`) is sent per bot. - Post-call processing events (`audio.processed`, `transcription.processed`, `video.processed`, `bot.done`, `data_deletion`) are sent **at most once**. --- ## 8) Minimal webhook handler checklist - ✅ Accept `POST` requests at `callback_url` - ✅ Verify signature (if enabled) - ✅ Always respond **2xx quickly** - ✅ Idempotent handling (store processed event IDs or de-dupe by `{bot_id, bot_event, timestamp}`) - ✅ On any terminal event (`bot.stopped` / `bot.kicked` / `bot.denied` / `bot.notallowed` / `bot.failed`), mark session complete - ✅ On `audio.processed` / `video.processed` / `transcription.processed`, fetch the corresponding artifact - ✅ On `data_deletion`, clean up local references --- If you want, share your preferred stack (Node/FastAPI/Cloudflare Workers), and I'll provide a ready-to-paste webhook handler example. # MeetStream Guide: Test Webhooks Locally with ngrok or Cloudflare Tunnel When you’re building a MeetStream integration, you’ll often want to test **webhooks locally** (on your laptop) before deploying a server. This guide shows how to: - run a local webhook server - expose it to the internet using **ngrok** or **Cloudflare Tunnel (cloudflared)** - paste the public URL into MeetStream as your `callback_url` ---
--- ## What you’re building 1. You run a local HTTP server (example: `http://localhost:3000/webhooks/meetstream`) 2. You create a tunnel that gives you a public URL (example: `https://abc123.ngrok-free.app/webhooks/meetstream`) 3. You use that public URL as MeetStream’s webhook `callback_url` --- ## 0) Create a simple local webhook endpoint Pick any stack. Here are two minimal examples. ### A) Node.js (Express) ```bash mkdir meetstream-webhook-test && cd meetstream-webhook-test npm init -y npm i express ``` Create `server.js`: ```js import express from "express"; const app = express(); app.use(express.json({ type: "*/*" })); app.post("/webhooks/meetstream", (req, res) => { console.log("✅ Webhook received"); console.log("Headers:", req.headers); console.log("Body:", req.body); res.status(200).send("ok"); }); app.listen(3000, () => console.log("Listening on http://localhost:3000")); ``` Run it: ```bash node server.js ``` Your local endpoint is now: - `http://localhost:3000/webhooks/meetstream` --- ### B) Python (FastAPI) ```bash pip install fastapi uvicorn ``` Create `app.py`: ```py from fastapi import FastAPI, Request app = FastAPI() @app.post("/webhooks/meetstream") async def webhook(req: Request): body = await req.json() print("✅ Webhook received") print("Headers:", dict(req.headers)) print("Body:", body) return {"ok": True} ``` Run it: ```bash uvicorn app:app --port 3000 --reload ``` Endpoint: - `http://localhost:3000/webhooks/meetstream` --- ## 1) Option A — Use ngrok (quickest) ### Step 1: Install ngrok - macOS (Homebrew): ```bash brew install ngrok/ngrok/ngrok ``` Or download from ngrok website and follow install steps. ### Step 2: Authenticate ngrok ```bash ngrok config add-authtoken ``` ### Step 3: Start a tunnel to your local port If your local server is running on `localhost:3000`: ```bash ngrok http 3000 ``` ngrok will print a public HTTPS URL like: - `https://abc123.ngrok-free.app` ### Step 4: Use the public URL as callback_url If your route is `/webhooks/meetstream`, your final webhook URL becomes: - `https://abc123.ngrok-free.app/webhooks/meetstream` Use this in your Create Bot payload: ```json { "callback_url": "https://abc123.ngrok-free.app/webhooks/meetstream" } ``` --- ## 2) Option B — Use Cloudflare Tunnel (cloudflared) Cloudflare Tunnel is great if you already use Cloudflare or want a stable tunnel. ### Step 1: Install cloudflared - macOS (Homebrew): ```bash brew install cloudflare/cloudflare/cloudflared ``` ### Step 2: Start a quick (temporary) tunnel If your local server is on port `3000`: ```bash cloudflared tunnel --url http://localhost:3000 ``` cloudflared will output a public URL like: - `https://something.trycloudflare.com` ### Step 3: Use it as callback_url Final webhook URL: - `https://something.trycloudflare.com/webhooks/meetstream` --- ## 3) Verify your local webhook is receiving events ### A) Add the URL while creating the bot Include in Create Bot payload: ```json { "callback_url": "https:///webhooks/meetstream" } ``` ### B) Trigger a bot lifecycle You should see incoming webhook payloads for events like: - `bot.joining` - `bot.inmeeting` - `bot.stopped` --- ## 4) Common issues & fixes ### Webhook not arriving? - Confirm your local server is running and the endpoint path matches exactly. - Ensure your tunnel is pointing to the right port (e.g., 3000). - Make sure your webhook handler returns **2xx** quickly. ### “502 Bad Gateway” in ngrok / Cloudflare - Your local server is not reachable on that port. - Try opening locally: - `curl -X POST http://localhost:3000/webhooks/meetstream -H "Content-Type: application/json" -d '{}'` ### Missing JSON body - Ensure you have JSON parsing enabled: - Express: `app.use(express.json({ type: "*/*" }))` - FastAPI already parses JSON with `await req.json()` --- ## 5) Production best practices (when you deploy) - Make webhook handling **idempotent** (avoid double-processing). - Verify webhook signatures (if configured). - Log webhook events + store minimal event history for debugging. - Don’t run tunnels in production — use a hosted endpoint. --- # MeetStream Guide: Create an AI Agent & Bring It Into a Meeting ## What is MIA? **MIA** stands for **MeetStream Infrastructure Agent** — the platform layer that lets you create, configure, and deploy AI agents into live meetings. Through the MIA tab in the dashboard, you define how your agent listens, thinks, and acts during a call. This guide walks you through creating an AI agent using MIA, connecting it to an MCP server for tool calling, and deploying it into a live meeting. Applies to **Google Meet, Zoom **and** Microsoft Teams** via a single unified API at [api.meetstream.ai](http://api.meetstream.ai) ---
--- ## 1) Open the MeetStream dashboard 1. Go to [**app.meetstream.ai**](https://app.meetstream.ai). 2. Navigate to the **MIA** tab. 3. Click **Create New Agent**. MIA tab — Create New Agent --- ## 2) Choose a mode: Realtime vs Pipeline MeetStream offers two agent modes. Pick the one that fits your use case. | | Realtime | Pipeline | | ---------------- | -------------------------------------------------------- | ----------------------------------------------------------------- | | **How it works** | Single provider handles everything (LLM, TTS, STT + MCP) | Each component (LLM, TTS, STT) can use a different provider | | **Latency** | Lower — fewer hops between services | Higher — each stage is a separate call | | **Best for** | Fast conversational agents where speed matters | Fine-tuned setups where you want the best provider per capability | > Tip: Start with **Realtime** mode if you want the fastest response times. Switch to **Pipeline** when you need specific provider combinations. --- ## 3) Select the agent response type Choose how the agent should interact during the meeting: | Response type | Behavior | | ------------- | --------------------------------------------------------- | | **Voice** | Agent listens and responds with spoken audio | | **Chat** | Agent responds via text in the meeting chat | | **Action** | Agent performs actions silently (no voice or chat output) | In all three modes, the agent can **perform tool actions** if an MCP server is connected (see step 6). --- ## 4) Select provider and model Pick the LLM provider and model that will power your agent. The available options depend on the mode you chose in step 2: - **Realtime mode** — select one provider/model for the entire pipeline. - **Pipeline mode** — select a provider/model individually for LLM, TTS, and STT. --- ## 5) Add a system prompt The system prompt defines **how your agent behaves** — its personality, constraints, and instructions. Write a clear prompt that tells the agent: - What role it plays (e.g., "You are a meeting assistant that takes notes and creates action items") - What it should and shouldn't do - How it should respond (tone, length, format) ### Example ``` You are a helpful meeting assistant for an engineering team. Summarize discussions, track action items, and create Linear tickets when asked. Keep responses concise and professional. ``` --- ## 6) Connect an MCP server (tool calling) MCP (Model Context Protocol) lets your agent call external tools — like creating tickets, querying databases, or triggering workflows. This section shows how to set up an MCP server locally using **Docker** and expose it to MeetStream. 1. Download and install the latest **Docker Desktop** from [***docker.com***](http://docker.com). 2. Sign in to Docker Desktop. 1. In Docker Desktop, look for the **MCP** tab (new feature). 2. Go to the **Catalogue** section. 3. Search for an MCP server — for example, **Linear**, **GitHub**, or any other available server. 4. Add the server and **authorize** it when prompted. Docker MCP Catalogue Start the Docker MCP gateway with streaming transport on port 8080: ```bash docker mcp gateway run --transport streaming --port 8080 ``` Docker will start the MCP gateway locally on port **8080**. You will also receive a **bearer token** in the output — save it, you'll need it in step 4. > Important: Copy and store the bearer token from the command output. You will need it to authenticate MeetStream with your MCP gateway. Your MCP gateway is running on `localhost:8080`, but MeetStream needs a public URL. Use ngrok to create a tunnel: ```bash ngrok http 8080 ``` ngrok will print a public HTTPS URL like: ``` https://c711-64-71-17-105.ngrok-free.app ``` Your MCP endpoint is now reachable at: ``` https://c711-64-71-17-105.ngrok-free.app/mcp ``` > For Docker MCP gateway, always append `/mcp` to the ngrok URL. Back in the MeetStream dashboard (agent creation screen): 1. In the **MCP Server URL** field, enter your full URL: ``` https://c711-64-71-17-105.ngrok-free.app/mcp ``` 2. In the **Header** section, add the bearer token: ``` Authorization: Bearer ``` 3. Click **Fetch** — MeetStream will connect to your MCP server and retrieve a list of available tools/actions. 4. Select the tools you want the agent to use (e.g., `create`, `list`, `edit`, etc.). 5. Click **Save** to finalize the agent. MCP Server Configuration ## 7) Bring the agent into a meeting Now that your agent is created, you can deploy it into a live meeting using the API. ### API endpoint ``` POST https://api.meetstream.ai/api/v1/bots/create_bot ``` ### Required fields Include these three fields in your payload alongside the standard bot parameters: | Field | Purpose | | ----------------------- | -------------------------------------------------------- | | `agent_config_id` | The ID of the agent you created in the dashboard | | `socket_connection_url` | WebSocket endpoint for the agent bridge connection | | `live_audio_required` | WebSocket endpoint for live audio streaming to the agent | ### Example cURL ```bash curl -X POST "https://api.meetstream.ai/api/v1/bots/create_bot" \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "meeting_link": "", "agent_config_id": "", "socket_connection_url": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge" }, "live_audio_required": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio" } }' ``` Once the bot joins the meeting, your AI agent is live — you can **start talking to it immediately**. --- ## 8) End-to-end summary Here's the full flow at a glance: 1. **Dashboard** → MIA tab → Create New Agent 2. **Mode** → Realtime (fast, single provider) or Pipeline (flexible, multi-provider) 3. **Response type** → Voice / Chat / Action 4. **Provider & model** → Pick the LLM (and TTS/STT if Pipeline) 5. **System prompt** → Define the agent's behavior 6. **MCP server** → Docker MCP gateway → ngrok tunnel → add URL + bearer token → fetch & select tools 7. **Deploy** → `POST /api/v1/bots/create_bot` with `agent_config_id` + WebSocket URLs 8. **Talk** → Agent is live in the meeting --- ## Troubleshooting ### MCP Fetch returns no tools - Confirm the Docker MCP gateway is running (`docker mcp gateway run ...`). - Confirm ngrok is tunneling to the correct port (8080). - Make sure you appended `/mcp` to the ngrok URL. - Verify the bearer token in the header is correct. ### Agent doesn't respond in the meeting - Check that `agent_config_id` matches the saved agent in the dashboard. - Ensure both WebSocket URLs are included in the `create_bot` payload. - Verify the meeting link is valid and the bot has joined successfully (check webhook events). ### ngrok tunnel expired - Free ngrok tunnels rotate URLs on restart. Re-run `ngrok http 8080` and update the MCP Server URL in the dashboard. --- For webhook event handling (bot lifecycle + post-call processing), see the [Webhook and Events Guide](https://docs.meetstream.ai/guides/webhooks/webhooks-and-events). --- ## FAQ **Meetstream Infrastructure Agent.** It's the platform layer that powers agent creation, configuration, and deployment into meetings. Provider and model selection is handled through the MIA dashboard. Contact support at docs.meetstream.ai for details on custom provider configurations. **Realtime** uses a single provider for LLM, TTS, STT, and MCP — it's faster because everything runs through one service. **Pipeline** lets you mix different providers for each component (e.g., one model for speech-to-text, another for the LLM), giving you more flexibility at the cost of slightly higher latency. Yes. As long as an MCP server is connected, the agent can execute tool actions regardless of whether its response type is set to Voice, Chat, or Action. Not necessarily. Docker is one way to run an MCP server locally using the built-in MCP catalog and gateway. If you already have an MCP-compatible server hosted elsewhere, you can point Meetstream directly to its URL. The Docker MCP gateway exposes its MCP endpoint at the `/mcp` path. Without it, MeetStream won't reach the correct route and the Fetch will fail. Currently, you configure one MCP server URL per agent. If you need tools from multiple sources, consider running a gateway that aggregates them behind a single endpoint. The agent loses access to MCP tools while the tunnel is down. It will still be in the meeting but won't be able to execute tool calls. Restart ngrok, update the MCP URL in the dashboard, and redeploy if needed. For production use, host your MCP server on a stable endpoint instead of a tunnel. After saving your agent in the MIA dashboard, the `agent_config_id` is shown in the agent details. You can also retrieve it via the MeetStream API. Yes. Go to the MIA tab in the dashboard, select your agent, and edit its settings (mode, prompt, MCP server, tools, etc.). Changes apply to new deployments — agents already in a meeting will use the configuration they were launched with. Google Meet, Zoom, and Microsoft Teams. # MeetStream — MIA API Reference MIA (MeetStream Infrastructure Agent) configurations define how an AI agent behaves when deployed into a meeting. Use these endpoints to create, retrieve, update, and delete agent configurations programmatically — no dashboard required. **Base URL:** `https://api.meetstream.ai` **Authentication:** `Authorization: Token ` **Content-Type:** `application/json` Applies to: **Google Meet, Zoom, Microsoft Teams** --- Agent configurations require that you have configured the relevant provider API keys in MeetStream. Add keys via the MeetStream dashboard → Integrations page (or the Integrations API) before creating an agent that references those providers. ## Endpoints | Method | Path | Description | |--------|------|-------------| | `POST` | `/api/v1/mia` | Create a new agent configuration | | `GET` | `/api/v1/mia` | Retrieve one or all agent configurations | | `PUT` | `/api/v1/mia` | Update an existing agent configuration | | `DELETE` | `/api/v1/mia` | Delete an agent configuration | --- ## POST /api/v1/mia Create a new agent configuration. ### Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | `agent_name` | `string` | Yes | Display name for this agent configuration | | `mode` | `string` | Yes | `"pipeline"` or `"realtime"` | | `model` | `object` | Yes | LLM or realtime model configuration (see below) | | `voice` | `object` | Pipeline only | TTS voice configuration (see below) | | `transcriber` | `object` | Pipeline only | STT transcription configuration (see below) | | `agent` | `object` | No | Agent behavior: response modality, first message, MCP servers | | `audio` | `object` | No | Audio processing configuration | | `wake_word` | `object` | No | Wake word gating — pipeline mode only | | `avatar` | `object` | No | Avatar configuration (Anam provider) | ### Nested: `model` (Pipeline Mode) | Field | Type | Required | Description | |-------|------|----------|-------------| | `provider` | `string` | Yes | LLM provider: `"openai"` or `"anthropic"` | | `model` | `string` | Yes | Model name — e.g. `"gpt-4.1"`, `"gpt-4.1-mini"` | | `system_prompt` | `string` | Yes | System prompt defining agent personality and behavior | | `temperature` | `number` | No | LLM temperature (0–2) | | `max_tokens` | `integer` | No | Maximum response tokens | ### Nested: `model` (Realtime Mode) | Field | Type | Required | Description | |-------|------|----------|-------------| | `provider` | `string` | Yes | Realtime provider: `"openai"`, `"xai"`, or `"google"` | | `model` | `string` | OpenAI only | Model name — e.g. `"gpt-4o-realtime-preview"` | | `system_prompt` | `string` | Yes | System prompt defining agent personality and behavior | | `voice` | `string` | Yes | Voice name. OpenAI: `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `nova`, `onyx`, `sage`, `shimmer`, `verse`. xAI: `Ara`, `Eve`, `Leo`, `Rex`, `Sal`. Google: `Puck`, `Charon`, `Kore`, `Fenrir`, `Aoede`, `Leda`, `Orus`, `Zephyr` | | `temperature` | `number` | No | Temperature (0–2) | | `max_response_output_tokens` | `number` | No | Max output tokens — OpenAI only, must be a positive number | | `thinking_config` | `object` | No | Google Gemini thinking: `{ "include_thoughts": bool, "thinking_budget": int }` | ### Nested: `voice` (Pipeline Mode) | Field | Type | Required | Description | |-------|------|----------|-------------| | `provider` | `string` | Yes | TTS provider: `"openai"` or `"elevenlabs"` | | `voice_id` | `string` | Yes | Voice identifier for the chosen provider | | `model` | `string` | No | TTS model name — e.g. `"tts-1"`, `"eleven_turbo_v2_5"` | ### Nested: `transcriber` (Pipeline Mode) | Field | Type | Required | Description | |-------|------|----------|-------------| | `provider` | `string` | Yes | STT provider: `"openai"`, `"deepgram"`, or `"assemblyai"` | | `model` | `string` | Yes | STT model name — e.g. `"nova-3"`, `"whisper-1"` | | `language` | `string` | No | Language code — e.g. `"en"`, `"es"` | | `boostwords` | `array[string]` | No | Words to boost recognition accuracy | ### Nested: `agent` | Field | Type | Required | Description | |-------|------|----------|-------------| | `response_type` | `string` | No | `"voice"`, `"chat"`, or `"action"` — defaults to `"voice"` | | `first_message` | `string` | No | Greeting spoken or sent as chat when the bot first joins | | `mcp_servers` | `array[object]` | No | MCP server configurations (see below) | ### Nested: `agent.mcp_servers[]` | Field | Type | Required | Description | |-------|------|----------|-------------| | `url` | `string` | Yes | Streamable HTTP MCP endpoint (must be HTTPS) | | `headers` | `object` | No | Authentication headers — e.g. `{ "Authorization": "Bearer " }` | | `allowed_tools` | `array[string]` | No | Whitelist of tool names. Omit to allow all tools from this server | | `timeout` | `number` | No | Per-call timeout in seconds (default: 10) | ### Nested: `wake_word` (Pipeline Mode Only) | Field | Type | Required | Description | |-------|------|----------|-------------| | `enabled` | `boolean` | No | Whether wake word gating is active | | `words` | `array[string]` or `string` | No | Trigger phrases. Comma-separated string or array — e.g. `["hey assistant", "hello bot"]` | | `timeout` | `number` | No | Seconds the agent stays active after the wake word is heard (default: 30) | ### Nested: `avatar` | Field | Type | Required | Description | |-------|------|----------|-------------| | `enabled` | `boolean` | No | Whether the avatar is active | | `provider` | `string` | No | Avatar provider — currently `"anam"` | | `avatar_id` | `string` | Required when provider is `"anam"` | Avatar identifier from the Anam platform | ### Example Request — Pipeline Mode ```bash curl -X POST https://api.meetstream.ai/api/v1/mia \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "agent_name": "Meeting Assistant", "mode": "pipeline", "model": { "provider": "openai", "model": "gpt-4.1", "system_prompt": "You are a helpful meeting assistant. Summarize key decisions and track action items." }, "voice": { "provider": "openai", "voice_id": "nova" }, "transcriber": { "provider": "deepgram", "model": "nova-3", "language": "en" }, "agent": { "response_type": "voice", "first_message": "Hello! I am your meeting assistant. How can I help today?" } }' ``` ### Example Request — Realtime Mode ```bash curl -X POST https://api.meetstream.ai/api/v1/mia \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "agent_name": "Fast Voice Agent", "mode": "realtime", "model": { "provider": "openai", "model": "gpt-4o-realtime-preview", "system_prompt": "You are a concise, helpful assistant.", "voice": "alloy" } }' ``` ### Example Request — Realtime Mode (xAI Grok) ```bash curl -X POST https://api.meetstream.ai/api/v1/mia \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "agent_name": "Grok Agent", "mode": "realtime", "model": { "provider": "xai", "system_prompt": "You are a witty and helpful meeting assistant.", "voice": "Ara" } }' ``` ### Example Request — Realtime Mode (Google Gemini) ```bash curl -X POST https://api.meetstream.ai/api/v1/mia \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "agent_name": "Gemini Agent", "mode": "realtime", "model": { "provider": "google", "system_prompt": "You are a thoughtful meeting assistant.", "voice": "Puck", "thinking_config": { "include_thoughts": true, "thinking_budget": 1024 } } }' ``` ### Example Request — Pipeline Mode with Wake Words ```bash curl -X POST https://api.meetstream.ai/api/v1/mia \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "agent_name": "Wake Word Agent", "mode": "pipeline", "model": { "provider": "openai", "model": "gpt-4.1", "system_prompt": "You are a helpful meeting assistant. Only respond when addressed." }, "voice": { "provider": "elevenlabs", "voice_id": "21m00Tcm4TlvDq8ikWAM" }, "transcriber": { "provider": "deepgram", "model": "nova-3" }, "wake_word": { "enabled": true, "words": ["hey assistant", "hello bot"], "timeout": 30 } }' ``` ### Example Request — Pipeline Mode with MCP Tools ```bash curl -X POST https://api.meetstream.ai/api/v1/mia \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "agent_name": "Action Agent", "mode": "pipeline", "model": { "provider": "openai", "model": "gpt-4.1", "system_prompt": "Listen for action items. When someone says to create a ticket, use the Linear tool to create it. Do not speak unless asked." }, "voice": { "provider": "elevenlabs", "voice_id": "21m00Tcm4TlvDq8ikWAM" }, "transcriber": { "provider": "deepgram", "model": "nova-3" }, "agent": { "response_type": "action", "mcp_servers": [ { "url": "https://your-mcp-gateway.example.com/mcp", "headers": { "Authorization": "Bearer " }, "allowed_tools": ["create_issue", "list_issues"] } ] } }' ``` ### Response — 200 OK ```json { "message": "Agent configuration created successfully.", "agent_config_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "agent_config": { "AgentConfigID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "UserID": "usr_xxxx", "AgentName": "Meeting Assistant", "Mode": "pipeline", "Model": { "provider": "openai", "model": "gpt-4.1", "system_prompt": "You are a helpful meeting assistant." }, "Voice": { "provider": "openai", "voice_id": "nova" }, "Transcriber": { "provider": "deepgram", "model": "nova-3", "language": "en" }, "CreatedAt": "2026-04-22T10:00:00Z", "UpdatedAt": "2026-04-22T10:00:00Z" } } ``` --- ## GET /api/v1/mia Retrieve a specific agent configuration by ID, or list all configurations for your account. ### Query Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `agent_config_id` | `string` | No | Retrieve a specific configuration by ID | If `agent_config_id` is omitted, all configurations for your account are returned. ### Example — List All Configurations ```bash curl -X GET https://api.meetstream.ai/api/v1/mia \ -H "Authorization: Token " ``` **200 OK** ```json { "agent_configs": [ { "AgentConfigID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "UserID": "usr_xxxx", "AgentName": "Meeting Assistant", "Mode": "pipeline", "Model": { "provider": "openai", "model": "gpt-4.1", "system_prompt": "..." }, "Voice": { "provider": "openai", "voice_id": "nova" }, "Transcriber": { "provider": "deepgram", "model": "nova-3" }, "CreatedAt": "2026-04-22T10:00:00Z", "UpdatedAt": "2026-04-22T10:00:00Z" } ], "count": 1 } ``` ### Example — Get a Specific Configuration ```bash curl -X GET "https://api.meetstream.ai/api/v1/mia?agent_config_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" \ -H "Authorization: Token " ``` **200 OK** ```json { "agent_config": { "AgentConfigID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "UserID": "usr_xxxx", "AgentName": "Meeting Assistant", "Mode": "pipeline", "Model": { "provider": "openai", "model": "gpt-4.1", "system_prompt": "..." }, "Voice": { "provider": "openai", "voice_id": "nova" }, "Transcriber": { "provider": "deepgram", "model": "nova-3" }, "Agent": { "response_type": "voice", "first_message": "Hello!" }, "CreatedAt": "2026-04-22T10:00:00Z", "UpdatedAt": "2026-04-22T10:00:00Z" } } ``` --- ## PUT /api/v1/mia Update an existing agent configuration. Only fields present in the request body are updated — omitted fields are left unchanged. ### Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | `agent_config_id` | `string` | Yes | ID of the configuration to update | | `agent_name` | `string` | No | New display name | | `mode` | `string` | No | Change mode: `"pipeline"` or `"realtime"`. Triggers full re-validation of all mode-specific fields. | | `model` | `object` | No | Replace the model configuration | | `voice` | `object` | No | Replace the voice configuration (pipeline mode) | | `transcriber` | `object` | No | Replace the transcription configuration (pipeline mode) | | `agent` | `object` | No | Replace agent behavior settings | | `wake_word` | `object` | No | Replace wake word configuration | | `audio` | `object` | No | Replace audio configuration | | `avatar` | `object` | No | Replace avatar configuration | > **Note:** Changing `mode` triggers full re-validation. Updating individual sections like `model`, `voice`, or `transcriber` validates that you have the required provider API keys but does not re-validate the full config structure. ### Example — Update the System Prompt ```bash curl -X PUT https://api.meetstream.ai/api/v1/mia \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "agent_config_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "model": { "provider": "openai", "model": "gpt-4.1-mini", "system_prompt": "You are a concise meeting assistant. Keep answers brief." } }' ``` ### Example — Update Agent Name and Wake Word ```bash curl -X PUT https://api.meetstream.ai/api/v1/mia \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "agent_config_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "agent_name": "Scrum Bot", "wake_word": { "enabled": true, "words": ["hey scrum bot"], "timeout": 45 } }' ``` ### Example — Switch from Pipeline to Realtime Mode ```bash curl -X PUT https://api.meetstream.ai/api/v1/mia \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "agent_config_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "mode": "realtime", "model": { "provider": "openai", "model": "gpt-4o-realtime-preview", "system_prompt": "You are a concise meeting assistant.", "voice": "alloy" } }' ``` ### Response — 200 OK ```json { "message": "Agent configuration updated successfully.", "agent_config": { "AgentConfigID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "UserID": "usr_xxxx", "AgentName": "Scrum Bot", "Mode": "pipeline", "Model": { "provider": "openai", "model": "gpt-4.1-mini", "system_prompt": "..." }, "Voice": { "provider": "openai", "voice_id": "nova" }, "Transcriber": { "provider": "deepgram", "model": "nova-3" }, "WakeWord": { "enabled": true, "words": ["hey scrum bot"], "timeout": 45 }, "UpdatedAt": "2026-04-22T11:00:00Z" } } ``` --- ## DELETE /api/v1/mia Permanently delete an agent configuration. This action cannot be undone. ### Query Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `agent_config_id` | `string` | Yes | ID of the configuration to delete | ### Example ```bash curl -X DELETE "https://api.meetstream.ai/api/v1/mia?agent_config_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" \ -H "Authorization: Token " ``` **200 OK** ```json { "message": "Agent configuration deleted successfully." } ``` --- ## Error Codes | HTTP Status | When it occurs | |-------------|----------------| | `400` | Missing required field, invalid value, unsupported provider, or provider API key not configured | | `403` | You do not own this agent configuration | | `404` | Agent configuration or user not found | | `405` | HTTP method not allowed on this path | | `500` | Internal server error | ### Common Error Responses **Missing provider API key** ```json { "message": "API key not configured for: model.provider=openai (missing OpenAIAPIKey). Add it via the Integrations API first." } ``` **Missing required field** ```json { "message": "Missing agent_name." } ``` **Invalid mode** ```json { "message": "Invalid mode: batch. Must be 'pipeline' or 'realtime'." } ``` **Unsupported provider** ```json { "message": "Unsupported model provider: cohere. Supported: ['openai', 'anthropic']" } ``` **Invalid realtime voice** ```json { "message": "Invalid OpenAI realtime voice: 'shimmy'. Supported: ['alloy', 'ash', 'ballad', 'coral', 'echo', 'fable', 'nova', 'onyx', 'sage', 'shimmer', 'verse']" } ``` **Invalid xAI voice** ```json { "message": "Invalid xAI voice: 'Bob'. Supported: ['Ara', 'Eve', 'Leo', 'Rex', 'Sal']" } ``` **Invalid Gemini voice** ```json { "message": "Invalid Gemini voice: 'Zeus'. Supported: ['Aoede', 'Charon', 'Fenrir', 'Kore', 'Leda', 'Orus', 'Puck', 'Zephyr']" } ``` **Access denied** ```json { "message": "Access denied to this agent configuration." } ``` --- ## Supported Providers Quick Reference ### Realtime Mode Providers | Provider | Voice Options | Notes | |----------|--------------|-------| | `openai` | `alloy`, `ash`, `ballad`, `coral`, `echo`, `fable`, `nova`, `onyx`, `sage`, `shimmer`, `verse` | Requires `model` field | | `xai` | `Ara`, `Eve`, `Leo`, `Rex`, `Sal` | Model is hardcoded server-side | | `google` | `Puck`, `Charon`, `Kore`, `Fenrir`, `Aoede`, `Leda`, `Orus`, `Zephyr` | Supports `thinking_config` | ### Pipeline Mode Providers | Component | Supported Providers | |-----------|-------------------| | Model (LLM) | `openai`, `anthropic` | | Voice (TTS) | `openai`, `elevenlabs` | | Transcriber (STT) | `openai`, `deepgram`, `assemblyai` | --- ## Using an Agent Config with a Bot Pass `agent_config_id` when creating a bot to attach the agent to a meeting session. The agent becomes active as soon as the bot joins. ```bash curl -X POST https://api.meetstream.ai/api/v1/bots/create_bot \ -H "Authorization: Token " \ -H "Content-Type: application/json" \ -d '{ "meeting_link": "https://meet.google.com/abc-defg-hij", "agent_config_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "socket_connection_url": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge" }, "live_audio_required": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio" } }' ``` See the [Create Bot Payload Reference](https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/create-bot) for all available bot creation options. --- ## Related Guides - [Create an AI Agent (Dashboard)](/guides/mia-meetstream-infrastructure-agents/create-mia) — Step-by-step dashboard walkthrough with MCP server setup - [Create Bot Payload Reference](https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/create-bot) — All `create_bot` fields including `agent_config_id` # Live Audio Capture & Frame Decoding MeetStream streams real-time meeting audio to your application over a WebSocket connection. Audio arrives as speaker-tagged binary frames from Google Meet, Zoom, and Microsoft Teams — all using the same wire format. --- ## Overview When you create a bot with the `live_audio_required` configuration, Meetstream opens a WebSocket connection from the bot to your server and continuously streams binary audio frames for the duration of the meeting. ### Enabling Live Audio Include `live_audio_required` in your Create Bot API request: ```json { "meeting_url": "https://meet.google.com/abc-defg-hij", "live_audio_required": { "websocket_url": "wss://your-server.com/audio" } } ``` The `websocket_url` is a WebSocket endpoint **you host**. Meetstream connects to it as a client. --- ## Connection Lifecycle ### 1. Bot connects to your WebSocket endpoint The bot initiates the connection when it joins the meeting. ### 2. Bot sends a JSON text handshake The first message is always a JSON text frame: ```json { "type": "ready", "bot_id": "bot_abc123", "message": "Ready to receive messages" } ``` ### 3. Binary audio frames stream continuously Every subsequent message is a **binary WebSocket frame** containing PCM audio with embedded speaker metadata. Frames arrive continuously for the duration of the meeting. ### 4. Connection closes when the bot leaves The WebSocket closes with a normal `1000` close code when the bot exits the meeting. ### Timeline ![Connection Lifecycle](https://files.buildwithfern.com/meetstream-ai-573402.docs.buildwithfern.com/f554c44f8926783c950916afa70281ea97f5f0459abc2f6ee31afd5897f7f6a5/docs/assets/images/connection-lifecycle.png) --- ## Binary Frame Format Every audio frame is a single binary WebSocket message with this structure: ``` ┌──────────┬────────────┬────────────┬──────────────┬──────────────┬──────────────────┐ │ msg_type │ sid_length │ speaker_id │ sname_length │ speaker_name │ pcm_audio_data │ │ 1 byte │ 2 bytes │ L1 bytes │ 2 bytes │ L2 bytes │ remaining bytes │ └──────────┴────────────┴────────────┴──────────────┴──────────────┴──────────────────┘ ``` ### Field-by-Field Breakdown | Offset | Size | Type | Field | Description | |--------|------|------|-------|-------------| | `0` | 1 byte | `uint8` | `msg_type` | Message type. Always `0x01` for PCM audio. | | `1` | 2 bytes | `uint16 LE` | `sid_length` | Byte length of the `speaker_id` string that follows. | | `3` | L1 bytes | `UTF-8` | `speaker_id` | Platform-specific unique identifier for the speaker. | | `3 + L1` | 2 bytes | `uint16 LE` | `sname_length` | Byte length of the `speaker_name` string that follows. | | `5 + L1` | L2 bytes | `UTF-8` | `speaker_name` | Display name of the speaker as shown in the meeting. | | `5 + L1 + L2` | remaining | `int16 LE` | `pcm_audio` | Raw PCM audio samples. | ### Important - There are **no delimiters** between fields. The format is length-prefixed: you read the 2-byte length, then read that many bytes for the string. - The `sid_length` and `sname_length` values change depending on the length of the speaker's name and ID. These are **not** fixed values or delimiters — they are standard unsigned 16-bit little-endian integers encoding a string length. - `0x01` is currently the only defined message type. All binary frames on this channel will have `0x01` at byte 0. --- ## Audio Properties | Property | Value | |----------|-------| | Encoding | Signed 16-bit integer (PCM16) | | Byte order | Little-endian | | Sample rate | 48,000 Hz | | Channels | 1 (mono) | | Bit depth | 16 bits (2 bytes per sample) | | Container | None — raw samples, no WAV/MP3/Ogg headers | To calculate duration from a frame: ``` duration_seconds = (len(pcm_audio_data) / 2) / 48000 ``` --- ## Hex Dump Walkthrough A frame from a speaker named `"Alice"` with ID `"user_42"`: ``` Hex: 01 07 00 75 73 65 72 5F 34 32 05 00 41 6C 69 63 65 XX XX XX XX ... ── ───── ───────────────────── ───── ─────────────── ───────────── │ │ │ │ │ │ │ │ │ │ │ └─ PCM16 LE audio samples │ │ │ │ └─ "Alice" (5 bytes UTF-8) │ │ │ └─ sname_length = 5 │ │ └─ "user_42" (7 bytes UTF-8) │ └─ sid_length = 7 └─ msg_type = 0x01 (PCM audio) ``` A frame from `"James Chen"` with ID `"James Chen"`: ``` Hex: 01 0A 00 4A 61 6D 65 73 20 43 68 65 6E 0A 00 4A 61 6D 65 73 20 43 68 65 6E XX XX ... ── ───── ────────────────────────────── ───── ────────────────────────────── ──────── │ │ │ │ │ │ │ │ │ │ │ └─ PCM audio │ │ │ │ └─ "James Chen" (10 bytes) │ │ │ └─ sname_length = 10 (0x0A) │ │ └─ "James Chen" (10 bytes) │ └─ sid_length = 10 (0x0A) └─ msg_type = 0x01 ``` Note: `0x07 = 7`, `0x0A = 10`, `0x0E = 14`, etc. These are string lengths, not protocol markers. --- ## Decoding Examples ### Python ```python def decode_audio_frame(data: bytes): """Decode a Meetstream binary audio frame. Returns: tuple: (speaker_id, speaker_name, pcm_bytes) on success None: if the frame is malformed """ if len(data) < 5 or data[0] != 0x01: return None # Speaker ID: 2-byte length prefix + UTF-8 string sid_len = int.from_bytes(data[1:3], "little") speaker_id = data[3 : 3 + sid_len].decode("utf-8") # Speaker Name: 2-byte length prefix + UTF-8 string off = 3 + sid_len sname_len = int.from_bytes(data[off : off + 2], "little") off += 2 speaker_name = data[off : off + sname_len].decode("utf-8") off += sname_len # Remaining bytes are raw PCM16 LE audio pcm_bytes = data[off:] return speaker_id, speaker_name, pcm_bytes ``` ### JavaScript / Node.js ```javascript function decodeAudioFrame(buffer) { if (buffer.length < 5 || buffer[0] !== 0x01) return null; // Speaker ID const sidLen = buffer.readUInt16LE(1); const speakerId = buffer.subarray(3, 3 + sidLen).toString("utf-8"); // Speaker Name let off = 3 + sidLen; const snameLen = buffer.readUInt16LE(off); off += 2; const speakerName = buffer.subarray(off, off + snameLen).toString("utf-8"); off += snameLen; // PCM audio const pcmData = buffer.subarray(off); return { speakerId, speakerName, pcmData }; } ``` ### Go ```go import ( "encoding/binary" "errors" ) type AudioFrame struct { SpeakerID string SpeakerName string PCMData []byte } func DecodeAudioFrame(data []byte) (*AudioFrame, error) { if len(data) < 5 || data[0] != 0x01 { return nil, errors.New("invalid frame") } sidLen := int(binary.LittleEndian.Uint16(data[1:3])) if len(data) < 3+sidLen+2 { return nil, errors.New("frame too short for speaker ID") } speakerID := string(data[3 : 3+sidLen]) off := 3 + sidLen snameLen := int(binary.LittleEndian.Uint16(data[off : off+2])) off += 2 if len(data) < off+snameLen { return nil, errors.New("frame too short for speaker name") } speakerName := string(data[off : off+snameLen]) off += snameLen return &AudioFrame{ SpeakerID: speakerID, SpeakerName: speakerName, PCMData: data[off:], }, nil } ``` ### Java ```java import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; public class MeetstreamAudioFrame { public final String speakerId; public final String speakerName; public final byte[] pcmData; private MeetstreamAudioFrame(String speakerId, String speakerName, byte[] pcmData) { this.speakerId = speakerId; this.speakerName = speakerName; this.pcmData = pcmData; } public static MeetstreamAudioFrame decode(byte[] data) { if (data.length < 5 || data[0] != 0x01) return null; ByteBuffer buf = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN); buf.get(); // skip msg_type int sidLen = buf.getShort() & 0xFFFF; byte[] sidBytes = new byte[sidLen]; buf.get(sidBytes); String speakerId = new String(sidBytes, StandardCharsets.UTF_8); int snameLen = buf.getShort() & 0xFFFF; byte[] snameBytes = new byte[snameLen]; buf.get(snameBytes); String speakerName = new String(snameBytes, StandardCharsets.UTF_8); byte[] pcmData = new byte[buf.remaining()]; buf.get(pcmData); return new MeetstreamAudioFrame(speakerId, speakerName, pcmData); } } ``` --- ## Full Receiver Examples ### Python — Receive and Log ```python import asyncio import json import websockets async def receive_audio(): async with websockets.connect("wss://your-server.com/audio") as ws: async for message in ws: # First message is a JSON text handshake if isinstance(message, str): handshake = json.loads(message) print(f"Bot connected: {handshake['bot_id']}") continue # All subsequent messages are binary audio frames result = decode_audio_frame(message) if result is None: continue speaker_id, speaker_name, pcm_bytes = result num_samples = len(pcm_bytes) // 2 duration_ms = (num_samples / 48000) * 1000 print(f"[{speaker_name}] {num_samples} samples ({duration_ms:.0f}ms)") asyncio.run(receive_audio()) ``` ### Node.js — Receive and Log ```javascript const WebSocket = require("ws"); const ws = new WebSocket("wss://your-server.com/audio"); ws.on("message", (data, isBinary) => { if (!isBinary) { const handshake = JSON.parse(data.toString()); console.log(`Bot connected: ${handshake.bot_id}`); return; } const frame = decodeAudioFrame(data); if (!frame) return; const numSamples = frame.pcmData.length / 2; const durationMs = (numSamples / 48000) * 1000; console.log(`[${frame.speakerName}] ${numSamples} samples (${durationMs.toFixed(0)}ms)`); }); ``` --- ## Working with PCM Audio ### Convert to NumPy Array (Python) ```python import numpy as np # To int16 sample array samples = np.frombuffer(pcm_bytes, dtype=np.int16) # To float32 (-1.0 to 1.0) — standard format for ML models and audio libraries float_samples = samples.astype(np.float32) / 32768.0 ``` ### Save as WAV File (Python) ```python import wave def save_wav(pcm_bytes: bytes, path: str, sample_rate: int = 48000): with wave.open(path, "wb") as wf: wf.setnchannels(1) wf.setsampwidth(2) wf.setframerate(sample_rate) wf.writeframes(pcm_bytes) ``` ### Accumulate and Save a Full Meeting Recording ```python import wave audio_buffer = bytearray() # Inside your receive loop: speaker_id, speaker_name, pcm_bytes = decode_audio_frame(message) audio_buffer.extend(pcm_bytes) # When the meeting ends: with wave.open("meeting_recording.wav", "wb") as wf: wf.setnchannels(1) wf.setsampwidth(2) wf.setframerate(48000) wf.writeframes(bytes(audio_buffer)) ``` ### Resample to a Different Rate (Python) Many speech-to-text services expect 16 kHz audio. Resample with `numpy`: ```python import numpy as np def resample_pcm16(pcm_bytes: bytes, src_rate: int, dst_rate: int) -> bytes: if src_rate == dst_rate: return pcm_bytes samples = np.frombuffer(pcm_bytes, dtype=np.int16).astype(np.float32) num_output = int(len(samples) * dst_rate / src_rate) t_in = np.linspace(0, 1, len(samples), endpoint=False) t_out = np.linspace(0, 1, num_output, endpoint=False) resampled = np.interp(t_out, t_in, samples) return np.clip(resampled, -32768, 32767).astype(np.int16).tobytes() # Example: 48kHz → 16kHz pcm_16k = resample_pcm16(pcm_bytes, 48000, 16000) ``` ### Convert to Int16 Array in JavaScript ```javascript function pcmBytesToInt16Array(buffer) { const int16 = new Int16Array(buffer.length / 2); for (let i = 0; i < int16.length; i++) { int16[i] = buffer.readInt16LE(i * 2); } return int16; } function pcmBytesToFloat32Array(buffer) { const float32 = new Float32Array(buffer.length / 2); for (let i = 0; i < float32.length; i++) { float32[i] = buffer.readInt16LE(i * 2) / 32768.0; } return float32; } ``` --- ## Speaker Identification Each frame includes both a `speaker_id` and a `speaker_name`: | Field | Description | Example | |-------|-------------|---------| | `speaker_id` | A platform-specific unique identifier. Stable within a session. | `"user_42"`, `"1234567890"`, `"NoSpeaker"` | | `speaker_name` | The display name shown in the meeting UI. May not be unique. | `"Alice"`, `"David Hill"`, `"NoSpeaker"` | ### Platform Behavior | Platform | `speaker_id` | `speaker_name` | |----------|-------------|----------------| | **Google Meet** | Participant ID from the meeting DOM | Display name from the meeting | | **Zoom** | Zoom SDK `node_id` (per-participant) | `GetUserName()` from the SDK | | **Teams** | Dominant speaker display name | Dominant speaker display name | ### Handling `"NoSpeaker"` If the bot cannot determine the active speaker, both fields will be `"NoSpeaker"`. This can happen during the first moments of a meeting or during mixed audio when speaker attribution is unavailable. --- ## Message Types on the Audio Channel | Byte 0 | Type | Format | Description | |--------|------|--------|-------------| | N/A | Handshake | JSON text | Sent once on connect. `{"type": "ready", ...}` | | `0x01` | PCM Audio | Binary | Speaker-tagged audio frame (described above) | `0x01` is currently the only binary message type. All binary frames will have `0x01` at position 0. Future protocol versions may introduce additional types — check byte 0 and skip unknown types for forward compatibility. --- ## FAQ 48,000 Hz for all platforms (Google Meet, Zoom, Teams). The audio is **mixed** — it contains all meeting participants combined into a single mono stream. Speaker metadata (`speaker_id`, `speaker_name`) indicates who was the **dominant speaker** when the frame was captured, but the audio itself contains everyone. Frame sizes vary. Typical frames contain 1,000 to 50,000+ samples (20ms to 1+ seconds of audio). The size depends on the platform's audio capture interval and buffering. No. The bot streams mixed audio. Use the `speaker_name` or `speaker_id` metadata for your own filtering or labeling logic after decoding. No. The protocol is fire-and-forget. The bot streams continuously and does not expect any response on the audio channel. Frames will buffer in the WebSocket layer. If the buffer grows too large, the connection may drop. Ensure your receiver processes or discards frames promptly. The 2-byte length field reflects the byte length of the speaker's ID string. Different speakers have different name/ID lengths: * `"Alice"` = 5 bytes → `sid_length` = `0x05 0x00` * `"James Chen"` = 10 bytes → `sid_length` = `0x0A 0x00` * `"Bob"` = 3 bytes → `sid_length` = `0x03 0x00` This is not a delimiter or protocol variation — it is a standard length-prefixed string encoding. Always read the 2-byte length, then read exactly that many bytes. Never hard-code expected length values. A correct parser: ```python sid_len = int.from_bytes(data[1:3], "little") # read length speaker_id = data[3 : 3 + sid_len].decode("utf-8") # read exactly that many bytes ``` An incorrect parser: ```python # BAD: hard-codes an expected "delimiter" byte if data[1] == 0x09 and data[2] == 0x00: ... ``` Yes. Speaker names are UTF-8 encoded. A name like `"Javier Martinez"` is 16 bytes, while `"佐藤太郎"` is 12 bytes (3 bytes per CJK character). Always use the length prefix — never scan for fixed byte patterns. Skip past the headers: ```python def extract_audio_only(data: bytes) -> bytes: if len(data) < 5 or data[0] != 0x01: return b"" sid_len = int.from_bytes(data[1:3], "little") off = 3 + sid_len sname_len = int.from_bytes(data[off:off + 2], "little") off += 2 + sname_len return data[off:] ``` # Live video streaming in MeetStream This guide explains how to receive **live video** from a MeetStream bot over WebSocket while a meeting is in progress. You provide a URL when you create the bot; MeetStream connects to your server and streams fragmented MP4 (`fMP4`) data you can record or process. --- ## Platform support Live video streaming is supported for **Google Meet** and **Microsoft Teams** meetings only. It is **not** available for other platforms (including Zoom) at this time. --- ## What you need - A **create bot** request that sets `video_required` to `true` and includes `live_video_required.websocket_url` (see below). - A **WebSocket server** you control that implements the message protocol in this document. - For local development, a way to expose that server on the public internet with **`wss://`** (for example ngrok or cloudflared). --- ## What happens during a session 1. You create a bot with a `meeting_link` for Google Meet or Teams and pass your WebSocket URL in the payload. 2. After the bot joins and recording starts, MeetStream opens a WebSocket connection to your URL. 3. You receive JSON control messages (`video_stream_start`, periodic `video_latency_ping`, and `video_stream_end`) and **binary frames** containing fMP4 chunks in order. 4. You respond to each `video_latency_ping` with `video_latency_pong` so latency can be measured. --- ## Create bot payload Use this shape when creating a bot in MeetStream: ```json { "meeting_link": "https://...", "video_required": true, "live_video_required": { "websocket_url": "wss://your-server.example.com/" } } ``` You can also use camelCase keys if your client prefers: `liveVideoRequired` and `websocketUrl`. ### WebSocket URL rules - Must be a string starting with `ws://` or `wss://`. - For production, use **`wss://`** (TLS). --- ## WebSocket protocol ### Messages from MeetStream (text / JSON) #### `video_stream_start` Sent once after the connection is established. ```json { "type": "video_stream_start", "bot_id": "bot-123", "codec": "h264", "audio_codec": "aac", "container": "fmp4", "width": 1920, "height": 1080, "framerate": 25, "audio_sample_rate": 44100, "audio_bitrate": "128k" } ``` #### `video_latency_ping` Sent periodically. ```json { "type": "video_latency_ping", "bot_id": "bot-123", "seq": 42, "sent_at_ms": 1743500000123 } ``` #### `video_stream_end` Sent when the stream is stopping. ```json { "type": "video_stream_end", "bot_id": "bot-123", "duration_seconds": 152.7 } ``` ### Messages from MeetStream (binary) - Raw fMP4 bytes from the recorder. Append chunks **in order** to build a continuous stream or file. ### Messages you send back (text / JSON) For every `video_latency_ping`, reply with: ```json { "type": "video_latency_pong", "seq": 42, "sent_at_ms": 1743500000123, "server_received_at_ms": 1743500000189, "bot_id": "bot-123" } ``` Echo the same `seq`, `sent_at_ms`, and `bot_id` from the ping; set `server_received_at_ms` to a millisecond timestamp when your server handled the ping. --- ## Implementing your WebSocket server You can use any stack that speaks WebSocket. Your server should: 1. Accept connections on `ws://` or `wss://`. 2. Parse text frames as JSON. 3. On `video_stream_start`, prepare your output (file, buffer, pipeline). 4. On each `video_latency_ping`, send a `video_latency_pong` as above. 5. On binary frames, append bytes in order to your output. 6. On `video_stream_end` or disconnect, finalize and close your output. ### Minimal handling loop 1. Track state per connection (for example `bot_id`, open output handle). 2. **Text JSON:** handle `video_stream_start`, `video_latency_ping`, `video_stream_end` as described. 3. **Binary:** append to the current output if `video_stream_start` was already received. 4. **Disconnect:** flush and close resources. ### Production tips - Terminate TLS in front of your app and expose **`wss://`** to MeetStream. - Allow large WebSocket frames if your platform has limits. - Process writes sequentially per stream so chunk order is preserved. - Isolate streams per bot or session. - Plan storage and backpressure for long meetings. --- ## Reference implementations ### Client side — creating a bot with live video Send a POST to the MeetStream API to create a bot with live video streaming enabled. The response includes the `bot_id` you will see in WebSocket messages. #### cURL ```bash curl -X POST https://api.meetstream.ai/api/bots \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "meeting_link": "https://meet.google.com/abc-defg-hij", "video_required": true, "live_video_required": { "websocket_url": "wss://your-server.example.com/" } }' ``` #### Node.js ```javascript const response = await fetch("https://api.meetstream.ai/api/bots", { method: "POST", headers: { "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json", }, body: JSON.stringify({ meeting_link: "https://meet.google.com/abc-defg-hij", video_required: true, live_video_required: { websocket_url: "wss://your-server.example.com/", }, }), }); const bot = await response.json(); console.log("Created bot:", bot.bot_id); ``` #### Python ```python import requests resp = requests.post( "https://api.meetstream.ai/api/bots", headers={ "Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json", }, json={ "meeting_link": "https://meet.google.com/abc-defg-hij", "video_required": True, "live_video_required": { "websocket_url": "wss://your-server.example.com/", }, }, ) bot = resp.json() print("Created bot:", bot["bot_id"]) ``` ### Server side — Node.js WebSocket receiver A complete receiver that writes incoming fMP4 chunks to `.mp4` files on disk. Each connection maps to one bot session. ```javascript import { createWriteStream } from "node:fs"; import { mkdir } from "node:fs/promises"; import { join } from "node:path"; import { WebSocketServer } from "ws"; const PORT = Number(process.env.WS_PORT || 9876); const HOST = process.env.WS_HOST || "0.0.0.0"; const OUT_DIR = process.env.OUTPUT_DIR || join(process.cwd(), "recordings"); await mkdir(OUT_DIR, { recursive: true }); const wss = new WebSocketServer({ host: HOST, port: PORT }); wss.on("connection", (ws) => { let writeStream = null; let botId = "bot"; let bytesWritten = 0; ws.on("message", (data, isBinary) => { // Binary frames: append fMP4 chunks to the output file if (isBinary) { if (!writeStream) return; const buf = Buffer.isBuffer(data) ? data : Buffer.from(data); bytesWritten += buf.length; writeStream.write(buf); return; } const msg = JSON.parse(data.toString()); if (msg.type === "video_stream_start") { botId = msg.bot_id || "unknown"; const outPath = join(OUT_DIR, `${botId}_${Date.now()}.mp4`); writeStream = createWriteStream(outPath); bytesWritten = 0; console.log(`Recording started -> ${outPath}`); } if (msg.type === "video_latency_ping") { ws.send(JSON.stringify({ type: "video_latency_pong", seq: msg.seq, sent_at_ms: msg.sent_at_ms, server_received_at_ms: Date.now(), bot_id: msg.bot_id || botId, })); } if (msg.type === "video_stream_end") { console.log(`Recording ended for ${botId} (${msg.duration_seconds}s)`); if (writeStream) { writeStream.end(); writeStream = null; } } }); ws.on("close", () => { if (writeStream) { writeStream.end(); writeStream = null; } }); }); console.log(`Listening on ws://${HOST}:${PORT}/`); ``` Run with: ```bash npm install ws node --experimental-modules server.mjs ``` ### Server side — Python WebSocket receiver The same receiver in Python using the `websockets` library. ```python import asyncio import json import os import time import websockets PORT = int(os.environ.get("WS_PORT", 9876)) HOST = os.environ.get("WS_HOST", "0.0.0.0") OUT_DIR = os.environ.get("OUTPUT_DIR", os.path.join(os.getcwd(), "recordings")) os.makedirs(OUT_DIR, exist_ok=True) async def handle(ws): out_file = None bot_id = "bot" bytes_written = 0 try: async for message in ws: # Binary frames: append fMP4 chunks to the output file if isinstance(message, bytes): if out_file is None: continue out_file.write(message) bytes_written += len(message) continue msg = json.loads(message) if msg["type"] == "video_stream_start": bot_id = msg.get("bot_id", "unknown") path = os.path.join(OUT_DIR, f"{bot_id}_{int(time.time())}.mp4") out_file = open(path, "wb") bytes_written = 0 print(f"Recording started -> {path}") elif msg["type"] == "video_latency_ping": await ws.send(json.dumps({ "type": "video_latency_pong", "seq": msg["seq"], "sent_at_ms": msg["sent_at_ms"], "server_received_at_ms": int(time.time() * 1000), "bot_id": msg.get("bot_id", bot_id), })) elif msg["type"] == "video_stream_end": print(f"Recording ended for {bot_id} ({msg.get('duration_seconds')}s)") if out_file: out_file.close() out_file = None finally: if out_file: out_file.close() async def main(): async with websockets.serve(handle, HOST, PORT): print(f"Listening on ws://{HOST}:{PORT}/") await asyncio.Future() asyncio.run(main()) ``` Run with: ```bash pip install websockets python server.py ``` --- ## Reaching a server on your laptop If MeetStream runs in the cloud and your receiver runs locally, expose the local WebSocket port with a tunnel (for example ngrok or cloudflared) and use the public **`wss://`** URL in `live_video_required.websocket_url`. Point the tunnel at the port your video receiver listens on. Do not reuse another WebSocket used for a different purpose. --- ## Troubleshooting | Symptom | What to check | |--------|----------------| | No connection or no data | Confirm `video_required` is `true`, the URL is correct, and the meeting is Google Meet or Teams. | | No binary chunks | Confirm the tunnel or firewall allows inbound connections and TLS is valid for `wss://`. | | TLS or certificate errors | Verify certificates, tunnel URL, and that you use `wss://` in production. | | Scheduled or recurring bots missing live video | Include `live_video_required` in every create-bot payload your automation sends. | Early in a session, the first bytes may arrive slightly before your WebSocket is fully ready; once connected, chunks should flow normally. --- ## Security - Prefer **`wss://`** in production. - Do not put secrets in URLs if you can avoid it; protect your receiver with auth, IP restrictions, or a private network where possible. - Treat tunnel URLs as sensitive while testing. --- ## Quick test checklist 1. Start your WebSocket receiver. 2. Expose it with a public `wss://` URL if needed. 3. Create a bot with a Google Meet or Teams `meeting_link` and `live_video_required.websocket_url` set to that URL. 4. Confirm you receive `video_stream_start`, then growing binary traffic. 5. Confirm you send `video_latency_pong` for each `video_latency_ping`. 6. Confirm `video_stream_end` when the session ends. If something still fails, note your `bot_id`, meeting platform, and timestamps when contacting MeetStream support. # External Meeting Control & Command Patterns Send audio, chat messages, images, and playback controls into a live meeting through Meetstream's control WebSocket channel. Works with Google Meet, Zoom, and Microsoft Teams. --- ## Overview When you create a bot with the `socket_connection_url` configuration, Meetstream opens a WebSocket connection from the bot to your server. You send JSON commands over this connection to control what the bot does inside the meeting. ### Enabling the Control Channel Include `socket_connection_url` in your Create Bot API request: ```json { "meeting_url": "https://meet.google.com/abc-defg-hij", "socket_connection_url": { "websocket_url": "wss://your-server.com/control" } } ``` The `websocket_url` is a WebSocket endpoint **you host**. Meetstream connects to it as a client. --- ## Connection Lifecycle ### 1. Bot connects to your WebSocket endpoint The bot initiates the connection when it joins the meeting. ### 2. Bot sends a JSON text handshake ```json { "type": "ready", "bot_id": "bot_abc123", "message": "Ready to receive messages" } ``` After you receive this, the bot is ready to accept commands. ### 3. You send JSON commands All commands are JSON text WebSocket frames. The bot executes them in the meeting. ### 4. Connection closes when the bot leaves The WebSocket closes with a normal `1000` close code when the bot exits. ### Timeline ![Control Command Lifecycle](https://files.buildwithfern.com/meetstream-ai-573402.docs.buildwithfern.com/683357c37e68a61ed6a829a9e54bf7bfe05c4574a94b1d7a671089dbfcba46e8/docs/assets/images/control-command-lifecycle.png) --- ## Command Reference All commands share this structure: ```json { "command": "", "bot_id": "", ...fields specific to the command } ``` --- ### `sendaudio` — Play Audio in the Meeting Plays audio through the bot's virtual microphone so all meeting participants hear it. Use this for text-to-speech output, pre-recorded audio prompts, or any audio your application generates. ```json { "command": "sendaudio", "bot_id": "bot_abc123", "audiochunk": "", "sample_rate": 48000, "encoding": "pcm16", "channels": 1, "endianness": "little" } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `command` | `string` | yes | `"sendaudio"` | | `bot_id` | `string` | yes | The bot identifier from the handshake | | `audiochunk` | `string` | yes | Base64-encoded audio bytes | | `sample_rate` | `int` | yes | Sample rate in Hz. **48000 recommended.** | | `encoding` | `string` | no | `"pcm16"` (only supported format) | | `channels` | `int` | no | `1` (mono only) | | `endianness` | `string` | no | `"little"` | #### Audio Encoding Requirements The `audiochunk` field must be a base64-encoded string of **raw PCM16 signed little-endian** audio bytes. No WAV headers, no MP3 — just raw samples. | Property | Value | |----------|-------| | Format | Signed 16-bit integer (PCM16) | | Byte order | Little-endian | | Sample rate | 48,000 Hz recommended | | Channels | 1 (mono) | | Container | None — raw samples only | | Transport encoding | Base64 | #### Encoding Audio — Python ```python import base64 import numpy as np def encode_pcm16_to_base64(pcm_bytes: bytes) -> str: """Encode raw PCM16 LE bytes to base64 for the sendaudio command.""" return base64.b64encode(pcm_bytes).decode("utf-8") def float32_to_pcm16_bytes(float_audio: np.ndarray) -> bytes: """Convert float32 audio (-1.0 to 1.0) to PCM16 LE bytes.""" clipped = np.clip(float_audio, -1.0, 1.0) return (clipped * 32767).astype(np.int16).tobytes() def resample_to_48k(pcm_bytes: bytes, source_rate: int) -> bytes: """Resample PCM16 LE audio from any sample rate to 48kHz.""" if source_rate == 48000: return pcm_bytes samples = np.frombuffer(pcm_bytes, dtype=np.int16).astype(np.float32) num_output = int(len(samples) * 48000 / source_rate) t_in = np.linspace(0, 1, len(samples), endpoint=False) t_out = np.linspace(0, 1, num_output, endpoint=False) resampled = np.interp(t_out, t_in, samples) return np.clip(resampled, -32768, 32767).astype(np.int16).tobytes() def build_sendaudio_command(bot_id: str, pcm_bytes: bytes, sample_rate: int = 48000) -> dict: """Build a complete sendaudio command ready to send over the WebSocket.""" return { "command": "sendaudio", "bot_id": bot_id, "audiochunk": encode_pcm16_to_base64(pcm_bytes), "sample_rate": sample_rate, "encoding": "pcm16", "channels": 1, "endianness": "little", } ``` #### Encoding Audio — JavaScript / Node.js ```javascript function encodePcm16ToBase64(pcmBuffer) { return pcmBuffer.toString("base64"); } function float32ToPcm16Buffer(float32Array) { const pcm = Buffer.alloc(float32Array.length * 2); for (let i = 0; i < float32Array.length; i++) { const clamped = Math.max(-1, Math.min(1, float32Array[i])); pcm.writeInt16LE(Math.round(clamped * 32767), i * 2); } return pcm; } function buildSendAudioCommand(botId, pcmBuffer, sampleRate = 48000) { return { command: "sendaudio", bot_id: botId, audiochunk: encodePcm16ToBase64(pcmBuffer), sample_rate: sampleRate, encoding: "pcm16", channels: 1, endianness: "little", }; } ``` #### Sending a WAV File — Python ```python import wave def send_wav_file(ws, bot_id: str, wav_path: str): """Read a WAV file and send it as a sendaudio command.""" with wave.open(wav_path, "rb") as wf: assert wf.getsampwidth() == 2, "WAV must be 16-bit" assert wf.getnchannels() == 1, "WAV must be mono" pcm_bytes = wf.readframes(wf.getnframes()) source_rate = wf.getframerate() pcm_48k = resample_to_48k(pcm_bytes, source_rate) command = build_sendaudio_command(bot_id, pcm_48k) ws.send(json.dumps(command)) ``` #### Chunked Audio Streaming For long audio (TTS streams, file playback), send audio in chunks rather than one large command. A good chunk size is 0.5–2 seconds: ```python CHUNK_SAMPLES = 48000 # 1 second at 48kHz CHUNK_BYTES = CHUNK_SAMPLES * 2 # 2 bytes per sample for i in range(0, len(pcm_bytes), CHUNK_BYTES): chunk = pcm_bytes[i : i + CHUNK_BYTES] command = build_sendaudio_command(bot_id, chunk) await ws.send(json.dumps(command)) await asyncio.sleep(0.8) # pace slightly below real-time to avoid queue gaps ``` --- ### `sendmsg` — Send a Chat Message Posts a text message in the meeting's chat panel, visible to all participants. ```json { "command": "sendmsg", "bot_id": "bot_abc123", "message": "Hello from the bot!", "msg": "Hello from the bot!" } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `command` | `string` | yes | `"sendmsg"` | | `bot_id` | `string` | yes | The bot identifier | | `message` | `string` | yes | The chat message text | | `msg` | `string` | yes | Same text as `message` | > **Why both `message` and `msg`?** Different platforms read different fields internally. Always include both with the same value for cross-platform compatibility. #### Example — Python ```python def build_chat_command(bot_id: str, text: str) -> dict: return { "command": "sendmsg", "bot_id": bot_id, "message": text, "msg": text, } await ws.send(json.dumps(build_chat_command("bot_abc123", "Meeting notes are ready!"))) ``` #### Example — JavaScript ```javascript function buildChatCommand(botId, text) { return { command: "sendmsg", bot_id: botId, message: text, msg: text, }; } ws.send(JSON.stringify(buildChatCommand("bot_abc123", "Meeting notes are ready!"))); ``` --- ### `sendchat` — Send a Chat Message with Role and Streaming An extended chat command that supports agent/user role tagging and incremental streaming. ```json { "command": "sendchat", "bot_id": "bot_abc123", "role": "assistant", "text": "Here is my response...", "is_final": true } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `command` | `string` | yes | `"sendchat"` | | `bot_id` | `string` | yes | The bot identifier | | `role` | `string` | yes | `"assistant"` for bot output, `"user"` for user transcript echo | | `text` | `string` | yes | The message text | | `is_final` | `bool` | yes | `false` for interim streaming tokens, `true` for the final committed message | #### Streaming Pattern Send partial messages as they are generated, then a final complete message: ```python # Stream tokens as they arrive for token in llm_stream: accumulated_text += token await ws.send(json.dumps({ "command": "sendchat", "bot_id": bot_id, "role": "assistant", "text": accumulated_text, "is_final": False, })) # Send the final complete message await ws.send(json.dumps({ "command": "sendchat", "bot_id": bot_id, "role": "assistant", "text": accumulated_text, "is_final": True, })) ``` --- ### `interrupt` — Stop Audio Playback Immediately stops any audio currently playing through the bot's speaker and clears the audio playback queue. Use this for barge-in (when a human starts speaking while the bot is talking) or to cancel a response. ```json { "command": "interrupt", "bot_id": "bot_abc123", "action": "clear_audio_queue" } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `command` | `string` | yes | `"interrupt"` | | `bot_id` | `string` | yes | The bot identifier | | `action` | `string` | yes | `"clear_audio_queue"` | #### Platform Support | Platform | Status | |----------|--------| | Google Meet | Fully supported — clears browser audio queue immediately | | Zoom | Not yet supported — command is accepted but audio queue is not cleared | | Teams | Not yet supported — command is accepted but audio queue is not cleared | #### Example — Barge-In Pattern ```python async def handle_barge_in(ws, bot_id: str): """Stop the bot's audio when a user starts speaking.""" await ws.send(json.dumps({ "command": "interrupt", "bot_id": bot_id, "action": "clear_audio_queue", })) ``` --- ### `sendimg` — Set Bot Video Frame (Base64) Sets the bot's camera feed to a static image. The image is displayed as the bot's video in the meeting. ```json { "command": "sendimg", "bot_id": "bot_abc123", "img": "" } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `command` | `string` | yes | `"sendimg"` | | `bot_id` | `string` | yes | The bot identifier | | `img` | `string` | yes | Base64-encoded image (JPEG or PNG) | #### Example — Python ```python import base64 def build_image_command(bot_id: str, image_path: str) -> dict: with open(image_path, "rb") as f: img_b64 = base64.b64encode(f.read()).decode("utf-8") return { "command": "sendimg", "bot_id": bot_id, "img": img_b64, } await ws.send(json.dumps(build_image_command("bot_abc123", "avatar.png"))) ``` --- ### `sendimg_url` — Set Bot Video Frame (URL) Same as `sendimg` but provides a URL instead of inline base64. The bot downloads the image and sets it as its video frame. ```json { "command": "sendimg_url", "bot_id": "bot_abc123", "img_url": "https://example.com/bot-avatar.png" } ``` | Field | Type | Required | Description | |-------|------|----------|-------------| | `command` | `string` | yes | `"sendimg_url"` | | `bot_id` | `string` | yes | The bot identifier | | `img_url` | `string` | yes | Publicly accessible URL to a JPEG or PNG image | --- ## Full Server Examples ### Python Server (FastAPI) A complete server that accepts both the audio and control channels, logs audio, and sends a welcome message. ```python import asyncio import base64 import json import numpy as np from fastapi import FastAPI, WebSocket, WebSocketDisconnect app = FastAPI() control_sockets: dict[str, WebSocket] = {} def decode_audio_frame(data: bytes): if len(data) < 5 or data[0] != 0x01: return None sid_len = int.from_bytes(data[1:3], "little") speaker_id = data[3 : 3 + sid_len].decode("utf-8") off = 3 + sid_len sname_len = int.from_bytes(data[off : off + 2], "little") off += 2 speaker_name = data[off : off + sname_len].decode("utf-8") off += sname_len return speaker_id, speaker_name, data[off:] async def send_command(bot_id: str, command: dict): ws = control_sockets.get(bot_id) if ws: await ws.send_text(json.dumps(command)) @app.websocket("/{bot_id}/audio") async def audio_endpoint(websocket: WebSocket, bot_id: str): """Receives live meeting audio from the Meetstream bot.""" await websocket.accept() try: while True: raw = await websocket.receive() if "text" in raw and raw["text"]: handshake = json.loads(raw["text"]) print(f"[{bot_id}] Audio handshake: {handshake}") continue if "bytes" in raw and raw["bytes"]: result = decode_audio_frame(raw["bytes"]) if result is None: continue speaker_id, speaker_name, pcm_bytes = result duration_ms = (len(pcm_bytes) / 2 / 48000) * 1000 print(f"[{bot_id}] [{speaker_name}] {duration_ms:.0f}ms audio") except WebSocketDisconnect: print(f"[{bot_id}] Audio disconnected") @app.websocket("/{bot_id}/control") async def control_endpoint(websocket: WebSocket, bot_id: str): """Receives commands from and sends commands to the Meetstream bot.""" await websocket.accept() control_sockets[bot_id] = websocket try: while True: text = await websocket.receive_text() data = json.loads(text) if data.get("type") == "ready": print(f"[{bot_id}] Control ready") # Send a welcome chat message await send_command(bot_id, { "command": "sendmsg", "bot_id": bot_id, "message": "Bot is now live!", "msg": "Bot is now live!", }) except WebSocketDisconnect: print(f"[{bot_id}] Control disconnected") finally: control_sockets.pop(bot_id, None) ``` Run: ```bash pip install fastapi uvicorn websockets numpy uvicorn server:app --host 0.0.0.0 --port 8000 ``` ### Node.js Server (ws) ```javascript const { WebSocketServer } = require("ws"); const http = require("http"); const server = http.createServer(); const wss = new WebSocketServer({ server }); const controlSockets = new Map(); function decodeAudioFrame(buf) { if (buf.length < 5 || buf[0] !== 0x01) return null; const sidLen = buf.readUInt16LE(1); const speakerId = buf.subarray(3, 3 + sidLen).toString("utf-8"); let off = 3 + sidLen; const snameLen = buf.readUInt16LE(off); off += 2; const speakerName = buf.subarray(off, off + snameLen).toString("utf-8"); off += snameLen; return { speakerId, speakerName, pcmData: buf.subarray(off) }; } function sendCommand(botId, command) { const ws = controlSockets.get(botId); if (ws && ws.readyState === 1) ws.send(JSON.stringify(command)); } wss.on("connection", (ws, req) => { const [, botId, channel] = req.url.split("/"); if (channel === "audio") { console.log(`[${botId}] Audio connected`); ws.on("message", (data, isBinary) => { if (!isBinary) { console.log(`[${botId}] Audio handshake:`, JSON.parse(data.toString())); return; } const frame = decodeAudioFrame(data); if (frame) { const ms = ((frame.pcmData.length / 2) / 48000 * 1000).toFixed(0); console.log(`[${botId}] [${frame.speakerName}] ${ms}ms`); } }); } else if (channel === "control") { controlSockets.set(botId, ws); console.log(`[${botId}] Control connected`); ws.on("message", (data) => { const msg = JSON.parse(data.toString()); if (msg.type === "ready") { console.log(`[${botId}] Control ready`); sendCommand(botId, { command: "sendmsg", bot_id: botId, message: "Bot is now live!", msg: "Bot is now live!", }); } }); ws.on("close", () => controlSockets.delete(botId)); } }); server.listen(8000, () => console.log("Listening on :8000")); ``` --- ## Using Both Channels Together When you enable both `live_audio_required` and `socket_connection_url`, the bot opens **two independent WebSocket connections** to your server. A typical Create Bot request: ```json { "meeting_url": "https://meet.google.com/abc-defg-hij", "bot_name": "My Assistant", "live_audio_required": { "websocket_url": "wss://your-server.com/{bot_id}/audio" }, "socket_connection_url": { "websocket_url": "wss://your-server.com/{bot_id}/control" } } ``` ### Pattern: Receive Audio → Process → Respond ```python # Audio channel: receive meeting audio speaker_id, speaker_name, pcm_bytes = decode_audio_frame(binary_message) # Your processing pipeline transcript = your_stt_service.transcribe(pcm_bytes) response = your_llm.generate(transcript) tts_audio = your_tts_service.synthesize(response) # Control channel: send response back into the meeting await send_command(bot_id, build_sendaudio_command(bot_id, tts_audio)) await send_command(bot_id, { "command": "sendchat", "bot_id": bot_id, "role": "assistant", "text": response, "is_final": True, }) ``` ### Pattern: Barge-In Detection ```python # When you detect the user started speaking while bot audio is playing: await send_command(bot_id, { "command": "interrupt", "bot_id": bot_id, "action": "clear_audio_queue", }) # Then process the new user input and generate a fresh response ``` --- ## Command Summary | Command | Purpose | Key Fields | |---------|---------|------------| | `sendaudio` | Play audio through the bot's speaker | `audiochunk` (base64 PCM16 LE), `sample_rate` | | `sendmsg` | Post a chat message | `message`, `msg` (same text in both) | | `sendchat` | Post a chat message with role and streaming | `role`, `text`, `is_final` | | `interrupt` | Stop audio playback and clear queue | `action: "clear_audio_queue"` | | `sendimg` | Set bot's video frame from base64 | `img` (base64 JPEG/PNG) | | `sendimg_url` | Set bot's video frame from URL | `img_url` | --- ## Audio Specs — Quick Reference | Direction | Format | Encoding | Sample Rate | Channels | Transport | |-----------|--------|----------|-------------|----------|-----------| | **Incoming** (meeting → you) | PCM16 signed LE | Raw binary | 48,000 Hz | 1 (mono) | Binary WebSocket frame | | **Outgoing** (you → meeting) | PCM16 signed LE | Base64 in JSON | 48,000 Hz recommended | 1 (mono) | JSON text WebSocket frame | # Bridge Server Architecture & Session Management Build a server that sits between MeetStream and your AI stack. The bridge receives real-time meeting audio, manages one session per MeetStream connection, routes audio through your processing pipeline, and sends responses (audio, chat, images) back into the meeting. --- ## Architecture ![MeetStream Bridge Architecture](https://files.buildwithfern.com/meetstream-ai-573402.docs.buildwithfern.com/29261295519f5bbc09f6e5e41138ea07295e1cb62ff4a5fc2ed3febb785bad17/docs/assets/images/bridge-architecture.png) The bridge server exposes **two WebSocket endpoints**. Each MeetStream connection opens one to each: | Endpoint | Direction | Format | Purpose | |----------|-----------|--------|---------| | `/bridge/audio` | MeetStream → You | Binary frames | Live meeting audio with speaker metadata | | `/bridge` | MeetStream ↔ You | JSON text | Commands: send audio, chat, images, interrupt | Each MeetStream connection is identified by a `bot_id`. The session manager maintains one AI session per `bot_id`, created on first connection and torn down when both WebSockets disconnect. --- ## Session Lifecycle ![MeetStream Bridge Session Lifecycle](https://files.buildwithfern.com/meetstream-ai-573402.docs.buildwithfern.com/0d87e2dc422ac80b1f681531ac45f5fc1c4be2d271e669ce1e11a8723a13804e/docs/assets/images/session-lifecycle.png) Key rules: - **Either channel** can arrive first. The session is created on whichever connects first. - **Both channels share** the same `bot_id` and the same AI session. - **Cleanup** happens when both WebSockets have disconnected for a given `bot_id`. --- ## Core Components ### 1. Session Manager The session manager is the central coordinator. It maps `bot_id` to: - An AI session (your STT/LLM/TTS pipeline) - References to the audio and control WebSockets - Per-session locks to prevent race conditions during creation ```python import asyncio import json import logging from typing import Any, Dict, Optional from fastapi import WebSocket from starlette.websockets import WebSocketState logger = logging.getLogger("bridge") class SessionManager: def __init__(self): self.sessions: Dict[str, Any] = {} self.audio_ws: Dict[str, WebSocket] = {} self.control_ws: Dict[str, WebSocket] = {} self._locks: Dict[str, asyncio.Lock] = {} def _lock_for(self, bot_id: str) -> asyncio.Lock: if bot_id not in self._locks: self._locks[bot_id] = asyncio.Lock() return self._locks[bot_id] async def ensure_session(self, bot_id: str): """Create an AI session for this bot_id if one doesn't exist.""" async with self._lock_for(bot_id): if bot_id in self.sessions: return session = await self._create_session(bot_id) self.sessions[bot_id] = session logger.info(f"[{bot_id}] Session created") async def close_session(self, bot_id: str): """Tear down the AI session for this bot_id.""" async with self._lock_for(bot_id): session = self.sessions.pop(bot_id, None) if session: await self._destroy_session(bot_id, session) logger.info(f"[{bot_id}] Session closed") async def maybe_cleanup(self, bot_id: str): """Close the session if both WebSockets have disconnected.""" if bot_id not in self.audio_ws and bot_id not in self.control_ws: await self.close_session(bot_id) async def _create_session(self, bot_id: str) -> Any: """ Replace this with your AI pipeline initialization. Examples: start an STT stream, create an LLM session, initialize a TTS engine, connect to a realtime API, etc. """ raise NotImplementedError("Implement _create_session()") async def _destroy_session(self, bot_id: str, session: Any): """ Replace this with your AI pipeline teardown. Close connections, flush buffers, release resources. """ raise NotImplementedError("Implement _destroy_session()") ``` ### 2. Audio Decoder Decodes the binary frame format from MeetStream. See [Live Audio Capture & Frame Decoding](/guides/web-sockets/real-time-audio-streaming) for the full specification. ```python from typing import Optional, Tuple def decode_audio_frame(data: bytes) -> Optional[Tuple[str, str, bytes]]: """Decode a MeetStream binary audio frame. Returns (speaker_id, speaker_name, pcm_bytes) or None. """ if len(data) < 5 or data[0] != 0x01: return None sid_len = int.from_bytes(data[1:3], "little") speaker_id = data[3 : 3 + sid_len].decode("utf-8") off = 3 + sid_len sname_len = int.from_bytes(data[off : off + 2], "little") off += 2 speaker_name = data[off : off + sname_len].decode("utf-8") off += sname_len return speaker_id, speaker_name, data[off:] ``` ### 3. Audio Resampler Meeting audio arrives at 48 kHz. Most AI models expect a different rate (e.g., 24 kHz for OpenAI Realtime, 16 kHz for Whisper). Resample before feeding your pipeline, and resample back to 48 kHz before sending to the meeting. ```python import numpy as np def resample_pcm16(pcm_bytes: bytes, src_hz: int, dst_hz: int) -> bytes: """Resample PCM16 LE audio between sample rates.""" if src_hz == dst_hz: return pcm_bytes x = np.frombuffer(pcm_bytes, dtype=np.int16).astype(np.float32) n_out = int(len(x) * dst_hz / src_hz) t_src = np.linspace(0, 1, len(x), endpoint=False) t_dst = np.linspace(0, 1, n_out, endpoint=False) y = np.interp(t_dst, t_src, x) return np.clip(y, -32768, 32767).astype(np.int16).tobytes() INCOMING_RATE = 48000 # MeetStream sends 48kHz MODEL_RATE = 24000 # Your model's expected rate OUTGOING_RATE = 48000 # MeetStream expects 48kHz back ``` ### 4. Speaker Filter Filter out MeetStream's own audio to prevent echo/feedback loops. The display name appears in the `speaker_name` field. ```python IGNORED_SPEAKERS = { "My Bot Name", "Meeting Assistant", } AGENT_KEYWORDS = ["bot", "agent", "assistant", "ai"] def should_ignore_speaker(speaker_name: str) -> bool: if speaker_name in IGNORED_SPEAKERS: return True lower = speaker_name.lower() return any(kw in lower for kw in AGENT_KEYWORDS) ``` ### 5. Command Sender Helper functions to build outbound commands for the control channel. See [Meeting Control & Command Patterns](/guides/web-sockets/meeting-control-and-command-patterns) for the full command reference. ```python import base64 async def safe_send(ws: WebSocket, payload: dict): """Send JSON to a WebSocket, swallowing errors.""" try: if ws.client_state == WebSocketState.CONNECTED: await ws.send_text(json.dumps(payload)) except Exception as e: logger.warning(f"WebSocket send failed: {e}") async def send_audio_to_meeting(ws: WebSocket, bot_id: str, pcm_bytes: bytes, sample_rate: int = 48000): await safe_send(ws, { "command": "sendaudio", "bot_id": bot_id, "audiochunk": base64.b64encode(pcm_bytes).decode("utf-8"), "sample_rate": sample_rate, "encoding": "pcm16", "channels": 1, "endianness": "little", }) async def send_chat_to_meeting(ws: WebSocket, bot_id: str, text: str): await safe_send(ws, { "command": "sendmsg", "bot_id": bot_id, "message": text, "msg": text, }) async def send_interrupt_to_meeting(ws: WebSocket, bot_id: str): await safe_send(ws, { "command": "interrupt", "bot_id": bot_id, "action": "clear_audio_queue", }) ``` --- ## WebSocket Endpoints ### Audio Endpoint: `/bridge/audio` Receives binary audio frames from the meeting. Decodes speaker metadata, filters out MeetStream's own audio, and routes PCM to your AI pipeline. ```python from fastapi import FastAPI, WebSocket, WebSocketDisconnect app = FastAPI() manager = SessionManager() @app.websocket("/bridge/audio") async def audio_endpoint(websocket: WebSocket): await websocket.accept() bot_id = None try: while True: raw = await websocket.receive() # Text frames: handshake or legacy JSON audio if "text" in raw and raw["text"]: data = json.loads(raw["text"]) # Handshake if data.get("type") == "ready": bot_id = data.get("bot_id") if not bot_id: await websocket.close(code=1003) return manager.audio_ws[bot_id] = websocket await manager.ensure_session(bot_id) await safe_send(websocket, { "type": "ack", "message": f"Audio channel bound to {bot_id}", }) continue # Legacy JSON audio (for backward compatibility) if data.get("type") == "PCMChunk" and bot_id: speaker = data.get("speakerName", "") if should_ignore_speaker(speaker): continue audio_b64 = data.get("audioData") if audio_b64: pcm = base64.b64decode(audio_b64) pcm_resampled = resample_pcm16(pcm, INCOMING_RATE, MODEL_RATE) session = manager.sessions.get(bot_id) if session: await session.send_audio(pcm_resampled) continue # Binary frames: current binary audio protocol if "bytes" in raw and raw["bytes"] and bot_id: result = decode_audio_frame(raw["bytes"]) if result is None: continue speaker_id, speaker_name, pcm_bytes = result if should_ignore_speaker(speaker_name): continue if pcm_bytes: pcm_resampled = resample_pcm16(pcm_bytes, INCOMING_RATE, MODEL_RATE) session = manager.sessions.get(bot_id) if session: await session.send_audio(pcm_resampled) except WebSocketDisconnect: pass except Exception as e: logger.error(f"[{bot_id}] Audio endpoint error: {e}") finally: if bot_id: manager.audio_ws.pop(bot_id, None) await manager.maybe_cleanup(bot_id) ``` ### Control Endpoint: `/bridge` Receives the MeetStream handshake and inbound commands (`usermsg`, `interrupt`). Your AI pipeline sends outbound commands (`sendaudio`, `sendmsg`, `sendchat`, `interrupt`) over this same connection. ```python @app.websocket("/bridge") async def control_endpoint(websocket: WebSocket): await websocket.accept() bot_id = None try: # Wait for handshake init = json.loads(await websocket.receive_text()) if init.get("type") != "ready" or not init.get("bot_id"): await websocket.close(code=1003) return bot_id = init["bot_id"] manager.control_ws[bot_id] = websocket await manager.ensure_session(bot_id) await safe_send(websocket, { "command": "ack", "bot_id": bot_id, "message": f"Control channel bound to {bot_id}", }) # Start the event pump that forwards AI output to MeetStream asyncio.create_task(event_pump(bot_id)) # Main loop: receive inbound commands from MeetStream while True: data = json.loads(await websocket.receive_text()) command = data.get("command") if command == "usermsg": text = data.get("message", "") if text: session = manager.sessions.get(bot_id) if session: await session.send_text(text) elif command == "interrupt": session = manager.sessions.get(bot_id) if session and hasattr(session, "interrupt"): await session.interrupt() except WebSocketDisconnect: pass except Exception as e: logger.error(f"[{bot_id}] Control endpoint error: {e}") finally: if bot_id: manager.control_ws.pop(bot_id, None) await manager.maybe_cleanup(bot_id) ``` --- ## Event Pump The event pump is a background task that continuously reads output from your AI pipeline and forwards it to the meeting through the control WebSocket. This is the outbound half of the bridge. ```python async def event_pump(bot_id: str): """Forward AI pipeline output to the MeetStream control channel.""" session = manager.sessions.get(bot_id) if not session: return try: async for event in session: ws = manager.control_ws.get(bot_id) if not ws or ws.client_state != WebSocketState.CONNECTED: continue # AI produced audio → send to meeting if event.type == "audio": pcm_model = event.audio_bytes # at MODEL_RATE pcm_out = resample_pcm16(pcm_model, MODEL_RATE, OUTGOING_RATE) await send_audio_to_meeting(ws, bot_id, pcm_out, OUTGOING_RATE) # AI speech was interrupted → clear the playback queue elif event.type == "audio_interrupted": await send_interrupt_to_meeting(ws, bot_id) # AI produced a text response → send as chat elif event.type == "text_response": await send_chat_to_meeting(ws, bot_id, event.text) except Exception as e: logger.error(f"[{bot_id}] Event pump error: {e}") ``` > **Note:** The event types above (`audio`, `audio_interrupted`, `text_response`) are illustrative. Replace them with whatever events your AI framework emits. --- ## Full Working Skeleton A minimal but complete bridge server. Replace the `YourAISession` class with your actual AI pipeline. ```python import asyncio import base64 import json import logging from contextlib import asynccontextmanager from typing import Any, Dict, Optional, Tuple import numpy as np from fastapi import FastAPI, WebSocket, WebSocketDisconnect from starlette.websockets import WebSocketState logging.basicConfig(level=logging.INFO) logger = logging.getLogger("bridge") # ── Configuration ───────────────────────────────────────────────────────────── INCOMING_RATE = 48000 # MeetStream sends 48kHz MODEL_RATE = 24000 # Your AI model's expected sample rate OUTGOING_RATE = 48000 # MeetStream expects 48kHz back IGNORED_SPEAKERS = {"My Bot Name"} AGENT_KEYWORDS = ["bot", "agent", "assistant"] # ── Audio utilities ─────────────────────────────────────────────────────────── def decode_audio_frame(data: bytes) -> Optional[Tuple[str, str, bytes]]: if len(data) < 5 or data[0] != 0x01: return None sid_len = int.from_bytes(data[1:3], "little") sid = data[3 : 3 + sid_len].decode("utf-8") off = 3 + sid_len sname_len = int.from_bytes(data[off : off + 2], "little") off += 2 sname = data[off : off + sname_len].decode("utf-8") off += sname_len return sid, sname, data[off:] def resample_pcm16(pcm_bytes: bytes, src_hz: int, dst_hz: int) -> bytes: if src_hz == dst_hz: return pcm_bytes x = np.frombuffer(pcm_bytes, dtype=np.int16).astype(np.float32) n_out = int(len(x) * dst_hz / src_hz) t_src = np.linspace(0, 1, len(x), endpoint=False) t_dst = np.linspace(0, 1, n_out, endpoint=False) y = np.interp(t_dst, t_src, x) return np.clip(y, -32768, 32767).astype(np.int16).tobytes() def should_ignore_speaker(name: str) -> bool: if name in IGNORED_SPEAKERS: return True lower = name.lower() return any(kw in lower for kw in AGENT_KEYWORDS) # ── Safe WebSocket send ─────────────────────────────────────────────────────── async def safe_send(ws: WebSocket, payload: dict): try: if ws.client_state == WebSocketState.CONNECTED: await ws.send_text(json.dumps(payload)) except Exception as e: logger.warning(f"send failed: {e}") # ── Your AI session (replace this) ─────────────────────────────────────────── class YourAISession: """ Stub. Replace with your actual AI pipeline. This could be: - An OpenAI Realtime API session - A Whisper STT + GPT + TTS pipeline - A LiveKit agent session - Any other audio-in/audio-out system """ async def send_audio(self, pcm_bytes: bytes): """Feed PCM audio into your pipeline.""" pass async def send_text(self, text: str): """Feed user text into your pipeline.""" pass async def interrupt(self): """Signal the pipeline to stop generating.""" pass async def events(self): """Yield output events from your pipeline. Expected event shapes (adapt to your framework): {"type": "audio", "pcm_bytes": bytes} {"type": "audio_interrupted"} {"type": "text", "content": str} """ while True: await asyncio.sleep(1) # replace with actual event stream return # ── Session Manager ─────────────────────────────────────────────────────────── class SessionManager: def __init__(self): self.sessions: Dict[str, YourAISession] = {} self.audio_ws: Dict[str, WebSocket] = {} self.control_ws: Dict[str, WebSocket] = {} self._locks: Dict[str, asyncio.Lock] = {} def _lock(self, bot_id: str) -> asyncio.Lock: if bot_id not in self._locks: self._locks[bot_id] = asyncio.Lock() return self._locks[bot_id] async def ensure_session(self, bot_id: str): async with self._lock(bot_id): if bot_id in self.sessions: return self.sessions[bot_id] = YourAISession() asyncio.create_task(self._event_pump(bot_id)) logger.info(f"[{bot_id}] Session created") async def close_session(self, bot_id: str): async with self._lock(bot_id): self.sessions.pop(bot_id, None) logger.info(f"[{bot_id}] Session closed") async def maybe_cleanup(self, bot_id: str): if bot_id not in self.audio_ws and bot_id not in self.control_ws: await self.close_session(bot_id) async def _event_pump(self, bot_id: str): """Background task: forward AI output → MeetStream control WS.""" session = self.sessions.get(bot_id) if not session: return try: async for event in session.events(): ws = self.control_ws.get(bot_id) if not ws or ws.client_state != WebSocketState.CONNECTED: continue etype = event.get("type") if etype == "audio": pcm_out = resample_pcm16(event["pcm_bytes"], MODEL_RATE, OUTGOING_RATE) await safe_send(ws, { "command": "sendaudio", "bot_id": bot_id, "audiochunk": base64.b64encode(pcm_out).decode("utf-8"), "sample_rate": OUTGOING_RATE, "encoding": "pcm16", "channels": 1, "endianness": "little", }) elif etype == "audio_interrupted": await safe_send(ws, { "command": "interrupt", "bot_id": bot_id, "action": "clear_audio_queue", }) elif etype == "text": await safe_send(ws, { "command": "sendmsg", "bot_id": bot_id, "message": event["content"], "msg": event["content"], }) except Exception as e: logger.error(f"[{bot_id}] Event pump error: {e}") manager = SessionManager() # ── FastAPI Application ─────────────────────────────────────────────────────── @asynccontextmanager async def lifespan(app: FastAPI): yield app = FastAPI(title="MeetStream Bridge", lifespan=lifespan) @app.get("/health") async def health(): return {"status": "healthy"} @app.websocket("/bridge/audio") async def audio_endpoint(websocket: WebSocket): """Receives live meeting audio from MeetStream.""" await websocket.accept() bot_id = None try: while True: raw = await websocket.receive() # Text frame: handshake or legacy JSON if "text" in raw and raw["text"]: data = json.loads(raw["text"]) if data.get("type") == "ready": bot_id = data.get("bot_id") if not bot_id: await websocket.close(code=1003) return manager.audio_ws[bot_id] = websocket await manager.ensure_session(bot_id) await safe_send(websocket, { "type": "ack", "message": f"Audio channel bound to {bot_id}", }) elif data.get("type") == "PCMChunk" and bot_id: speaker = data.get("speakerName", "") if should_ignore_speaker(speaker): continue b64 = data.get("audioData") if b64: pcm = base64.b64decode(b64) pcm_r = resample_pcm16(pcm, INCOMING_RATE, MODEL_RATE) session = manager.sessions.get(bot_id) if session: await session.send_audio(pcm_r) continue # Binary frame: current protocol if "bytes" in raw and raw["bytes"] and bot_id: result = decode_audio_frame(raw["bytes"]) if result is None: continue _, speaker_name, pcm_bytes = result if should_ignore_speaker(speaker_name): continue if pcm_bytes: pcm_r = resample_pcm16(pcm_bytes, INCOMING_RATE, MODEL_RATE) session = manager.sessions.get(bot_id) if session: await session.send_audio(pcm_r) except WebSocketDisconnect: pass except Exception as e: logger.error(f"[{bot_id}] Audio error: {e}") finally: if bot_id: manager.audio_ws.pop(bot_id, None) await manager.maybe_cleanup(bot_id) @app.websocket("/bridge") async def control_endpoint(websocket: WebSocket): """Two-way command channel with MeetStream.""" await websocket.accept() bot_id = None try: init = json.loads(await websocket.receive_text()) if init.get("type") != "ready" or not init.get("bot_id"): await websocket.close(code=1003) return bot_id = init["bot_id"] manager.control_ws[bot_id] = websocket await manager.ensure_session(bot_id) await safe_send(websocket, { "command": "ack", "bot_id": bot_id, "message": f"Control channel bound to {bot_id}", }) while True: data = json.loads(await websocket.receive_text()) command = data.get("command") if command == "usermsg": text = data.get("message", "") if text: session = manager.sessions.get(bot_id) if session: await session.send_text(text) elif command == "interrupt": session = manager.sessions.get(bot_id) if session: await session.interrupt() except WebSocketDisconnect: pass except Exception as e: logger.error(f"[{bot_id}] Control error: {e}") finally: if bot_id: manager.control_ws.pop(bot_id, None) await manager.maybe_cleanup(bot_id) if __name__ == "__main__": import uvicorn uvicorn.run("server:app", host="0.0.0.0", port=8000, reload=True) ``` Run: ```bash pip install fastapi uvicorn numpy uvicorn server:app --host 0.0.0.0 --port 8000 ``` --- ## Connecting MeetStream When creating a MeetStream session, point both WebSocket URLs at your bridge server: ```json { "meeting_url": "https://meet.google.com/abc-defg-hij", "bot_name": "My Assistant", "live_audio_required": { "websocket_url": "wss://your-bridge.com/bridge/audio" }, "socket_connection_url": { "websocket_url": "wss://your-bridge.com/bridge" } } ``` Both connections carry the same `bot_id` in their handshake, so the session manager can link them together. --- ## Design Decisions Separation of concerns. The audio channel is high-throughput binary data (hundreds of frames per minute). The control channel is low-frequency JSON commands. Splitting them avoids head-of-line blocking and makes it easier to handle each independently. Backward compatibility. Older MeetStream versions send audio as JSON `PCMChunk` messages with base64-encoded audio. Current versions send binary frames (significantly more efficient). The bridge should accept both. Both WebSocket connections race to `ensure_session()`. Without a lock, two AI sessions could be created for the same `bot_id`. The async lock ensures exactly one session is created. A single reconnecting WebSocket shouldn't destroy the running session. The session stays alive as long as at least one channel is connected. MeetStream captures audio at 48 kHz (the standard WebAudio and Zoom SDK rate). Your model may need 16 kHz or 24 kHz. On the return path, MeetStream's virtual speaker operates at 48 kHz. The bridge handles both conversions so neither MeetStream nor your model needs to know about the other's sample rate. ## Related Documentation - [Live Audio Capture & Frame Decoding](/guides/web-sockets/real-time-audio-streaming) — Binary frame format specification, decode examples in 4 languages, FAQ - [Meeting Control & Command Patterns](/guides/web-sockets/meeting-control-and-command-patterns) — Full command reference for `sendaudio`, `sendmsg`, `sendchat`, `interrupt`, `sendimg`, `sendimg_url` # Create Bot POST https://api.meetstream.ai/api/v1/bots/create_bot Content-Type: application/json This endpoint supports 20 different payload variations. Use the dropdown above the code panel to explore configurations for various use cases. Reference: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/create-bot ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/bots/create_bot: post: operationId: create-bot summary: Create Bot description: >- This endpoint supports 20 different payload variations. Use the dropdown above the code panel to explore configurations for various use cases. tags: - subpackage_bots parameters: - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '201': description: Created content: application/json: schema: $ref: '#/components/schemas/BotResponse' requestBody: content: application/json: schema: $ref: '#/components/schemas/CreateBotRequest' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: CreateBotRequestZoom: type: object properties: use_zoom_obf: type: boolean description: Enable Zoom OBF (On-Behalf-Of) feature. description: Zoom-specific configuration options. title: CreateBotRequestZoom CreateBotRequestSocketConnectionUrl: type: object properties: websocket_url: type: string description: WebSocket URL for two-way audio/data bridge connection. title: CreateBotRequestSocketConnectionUrl CreateBotRequestLiveAudioRequired: type: object properties: websocket_url: type: string description: WebSocket URL to stream live audio output from the meeting. title: CreateBotRequestLiveAudioRequired CreateBotRequestLiveVideoRequired: type: object properties: websocket_url: type: string description: WebSocket URL to stream live video output from the meeting. title: CreateBotRequestLiveVideoRequired CreateBotRequestLiveTranscriptionRequired: type: object properties: webhook_url: type: string description: Webhook URL to receive live transcription events. title: CreateBotRequestLiveTranscriptionRequired AutomaticLeaveConfig: type: object properties: waiting_room_timeout: type: integer description: |- Seconds to wait in a waiting room before leaving. (Default Value: 600 seconds / 10 min) everyone_left_timeout: type: integer description: >- Seconds to stay in the call after everyone else leaves. (Default Value: 300 seconds / 5 minutes) voice_inactivity_timeout: type: integer description: >- Seconds to wait if no audio is detected. (Default Value: 100 seconds / 1 minute and 40 seconds) in_call_recording_timeout: type: integer description: >- Maximum seconds to stay in call while recording. (Default Value: 14400 seconds / 4 hours) recording_permission_denied_timeout: type: integer description: >- Seconds to wait if recording permission is denied. This is specific to Zoom only. (Default Value: 60 seconds / 1 minute). description: >- Configure how long the bot should remain in the meeting under different conditions before leaving automatically. All timeout values are specified in seconds and accept integer values only. title: AutomaticLeaveConfig RecordingConfigRetentionType: type: string enum: - timed default: timed title: RecordingConfigRetentionType RecordingConfigRetention: type: object properties: type: $ref: '#/components/schemas/RecordingConfigRetentionType' default: timed hours: type: integer default: 24 title: RecordingConfigRetention RecordingConfigTranscriptProviderDeepgramModel: type: string enum: - nova-3 default: nova-3 title: RecordingConfigTranscriptProviderDeepgramModel RecordingConfigTranscriptProviderDeepgram: type: object properties: model: $ref: '#/components/schemas/RecordingConfigTranscriptProviderDeepgramModel' language: type: string default: en punctuate: type: boolean default: true smart_format: type: boolean default: true diarize: type: boolean default: true paragraphs: type: boolean default: true numerals: type: boolean default: true filler_words: type: boolean default: false utterances: type: boolean default: true utt_split: type: number format: double default: 0.8 detect_language: type: boolean default: false keywords: type: array items: type: string search: type: array items: type: string tag: type: array items: type: string required: - model - language - punctuate - smart_format - diarize - paragraphs - numerals - filler_words - utterances - utt_split - detect_language title: RecordingConfigTranscriptProviderDeepgram RecordingConfigTranscriptProviderAssemblyaiSpeechModelsItems: type: string enum: - universal-2 title: RecordingConfigTranscriptProviderAssemblyaiSpeechModelsItems RecordingConfigTranscriptProviderAssemblyai: type: object properties: speech_models: type: array items: $ref: >- #/components/schemas/RecordingConfigTranscriptProviderAssemblyaiSpeechModelsItems language_code: type: string default: en_us speaker_labels: type: boolean default: true punctuate: type: boolean default: true format_text: type: boolean default: true filter_profanity: type: boolean default: false redact_pii: type: boolean default: false auto_chapters: type: boolean default: false entity_detection: type: boolean default: false keyterms_prompt: type: array items: type: string required: - speech_models - language_code - speaker_labels - punctuate - format_text - filter_profanity - redact_pii - auto_chapters - entity_detection title: RecordingConfigTranscriptProviderAssemblyai RecordingConfigTranscriptProviderJigsawstack: type: object properties: language: type: string default: auto translate: type: boolean default: false by_speaker: type: boolean default: true required: - language - translate title: RecordingConfigTranscriptProviderJigsawstack RecordingConfigTranscriptProviderSarvamModel: type: string enum: - saaras:v3 default: saaras:v3 title: RecordingConfigTranscriptProviderSarvamModel RecordingConfigTranscriptProviderSarvamMode: type: string enum: - transcribe - translate default: transcribe title: RecordingConfigTranscriptProviderSarvamMode RecordingConfigTranscriptProviderSarvam: type: object properties: model: $ref: '#/components/schemas/RecordingConfigTranscriptProviderSarvamModel' language_code: type: string default: en-IN mode: $ref: '#/components/schemas/RecordingConfigTranscriptProviderSarvamMode' with_diarization: type: boolean default: true required: - model - language_code - mode - with_diarization title: RecordingConfigTranscriptProviderSarvam RecordingConfigTranscriptProviderMeetingCaptions: type: object properties: {} description: >- Uses the meeting platform's native captions. No additional config required. title: RecordingConfigTranscriptProviderMeetingCaptions RecordingConfigTranscriptProviderMeetstream: type: object properties: language: type: string default: auto translate: type: boolean default: false required: - language - translate title: RecordingConfigTranscriptProviderMeetstream RecordingConfigTranscriptProviderAssemblyaiStreamingSpeechModel: type: string enum: - universal-streaming-english default: universal-streaming-english title: RecordingConfigTranscriptProviderAssemblyaiStreamingSpeechModel RecordingConfigTranscriptProviderAssemblyaiStreamingTranscriptionMode: type: string enum: - raw - sentence default: raw title: RecordingConfigTranscriptProviderAssemblyaiStreamingTranscriptionMode RecordingConfigTranscriptProviderAssemblyaiStreamingEncoding: type: string enum: - pcm_s16le default: pcm_s16le title: RecordingConfigTranscriptProviderAssemblyaiStreamingEncoding RecordingConfigTranscriptProviderAssemblyaiStreaming: type: object properties: speech_model: $ref: >- #/components/schemas/RecordingConfigTranscriptProviderAssemblyaiStreamingSpeechModel transcription_mode: $ref: >- #/components/schemas/RecordingConfigTranscriptProviderAssemblyaiStreamingTranscriptionMode sample_rate: type: integer default: 48000 format_turns: type: boolean default: false encoding: $ref: >- #/components/schemas/RecordingConfigTranscriptProviderAssemblyaiStreamingEncoding vad_threshold: type: string default: '0.4' end_of_turn_confidence_threshold: type: string default: '0.4' inactivity_timeout: type: integer default: 300 min_end_of_turn_silence_when_confident: type: string default: '400' max_turn_silence: type: string default: '1280' required: - speech_model - transcription_mode - sample_rate - format_turns - encoding - vad_threshold - end_of_turn_confidence_threshold - inactivity_timeout - min_end_of_turn_silence_when_confident - max_turn_silence title: RecordingConfigTranscriptProviderAssemblyaiStreaming RecordingConfigTranscriptProviderDeepgramStreamingModel: type: string enum: - nova-2 default: nova-2 title: RecordingConfigTranscriptProviderDeepgramStreamingModel RecordingConfigTranscriptProviderDeepgramStreamingTranscriptionMode: type: string enum: - sentence - word - raw default: sentence title: RecordingConfigTranscriptProviderDeepgramStreamingTranscriptionMode RecordingConfigTranscriptProviderDeepgramStreamingEncoding: type: string enum: - linear16 default: linear16 title: RecordingConfigTranscriptProviderDeepgramStreamingEncoding RecordingConfigTranscriptProviderDeepgramStreaming: type: object properties: model: $ref: >- #/components/schemas/RecordingConfigTranscriptProviderDeepgramStreamingModel transcription_mode: $ref: >- #/components/schemas/RecordingConfigTranscriptProviderDeepgramStreamingTranscriptionMode language: type: string default: en punctuate: type: boolean default: true smart_format: type: boolean default: true endpointing: type: integer default: 300 vad_events: type: boolean default: true utterance_end_ms: type: integer default: 1000 encoding: $ref: >- #/components/schemas/RecordingConfigTranscriptProviderDeepgramStreamingEncoding channels: type: integer default: 1 required: - model - transcription_mode - language - punctuate - smart_format - endpointing - vad_events - utterance_end_ms - encoding - channels title: RecordingConfigTranscriptProviderDeepgramStreaming RecordingConfigTranscriptProvider: type: object properties: deepgram: $ref: '#/components/schemas/RecordingConfigTranscriptProviderDeepgram' assemblyai: $ref: '#/components/schemas/RecordingConfigTranscriptProviderAssemblyai' jigsawstack: $ref: '#/components/schemas/RecordingConfigTranscriptProviderJigsawstack' sarvam: $ref: '#/components/schemas/RecordingConfigTranscriptProviderSarvam' meeting_captions: $ref: >- #/components/schemas/RecordingConfigTranscriptProviderMeetingCaptions description: >- Uses the meeting platform's native captions. No additional config required. meetstream: $ref: '#/components/schemas/RecordingConfigTranscriptProviderMeetstream' assemblyai_streaming: $ref: >- #/components/schemas/RecordingConfigTranscriptProviderAssemblyaiStreaming deepgram_streaming: $ref: >- #/components/schemas/RecordingConfigTranscriptProviderDeepgramStreaming description: Transcription provider config. Use one provider key per request. title: RecordingConfigTranscriptProvider RecordingConfigTranscript: type: object properties: provider: $ref: '#/components/schemas/RecordingConfigTranscriptProvider' description: Transcription provider config. Use one provider key per request. required: - provider title: RecordingConfigTranscript RecordingConfigRealtimeEndpointsItems: type: object properties: type: type: string url: type: string events: type: array items: type: string title: RecordingConfigRealtimeEndpointsItems RecordingConfig: type: object properties: retention: $ref: '#/components/schemas/RecordingConfigRetention' transcript: $ref: '#/components/schemas/RecordingConfigTranscript' realtime_endpoints: type: array items: $ref: '#/components/schemas/RecordingConfigRealtimeEndpointsItems' title: RecordingConfig CreateBotRequest: type: object properties: meeting_link: type: string description: The URL of the Google Meet, Zoom, or Teams meeting. bot_name: type: string description: The display name the bot will use in the meeting. video_required: type: boolean default: true description: Whether the bot should record video. zoom: $ref: '#/components/schemas/CreateBotRequestZoom' description: Zoom-specific configuration options. audio_separate_streams: type: boolean description: Enable separate audio streams feature. video_separate_streams: type: boolean description: Enable separate video streams feature. bot_message: type: string description: An initial message for the bot to post in the meeting chat. bot_image_url: type: string description: URL for the bot's profile picture or avatar. callback_url: type: string description: A URL to receive webhook event callbacks. join_at: type: string format: date-time description: ISO 8601 datetime to schedule the bot to join the meeting. agent_config_id: type: string description: The ID of the agent configuration to enable agentic features. custom_attributes: type: object additionalProperties: description: Any type description: Arbitrary key-value metadata attached to the bot. socket_connection_url: $ref: '#/components/schemas/CreateBotRequestSocketConnectionUrl' description: WebSocket URL for two-way audio/data bridge connection. live_audio_required: $ref: '#/components/schemas/CreateBotRequestLiveAudioRequired' description: WebSocket URL to stream live audio output from the meeting. live_video_required: $ref: '#/components/schemas/CreateBotRequestLiveVideoRequired' description: WebSocket URL to stream live video output from the meeting. workflow_config_ids: type: array items: type: string description: List of workflow configuration IDs to trigger post-meeting. live_transcription_required: $ref: '#/components/schemas/CreateBotRequestLiveTranscriptionRequired' description: Webhook URL to receive live transcription events. automatic_leave: $ref: '#/components/schemas/AutomaticLeaveConfig' recording_config: $ref: '#/components/schemas/RecordingConfig' required: - meeting_link - bot_name title: CreateBotRequest BotResponse: type: object properties: bot_id: type: string transcript_id: type: - string - 'null' meeting_url: type: string status: type: string title: BotResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples ### Create Bot — Full Payload **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "bot_message": "Hey Everyone :wave , I'm a speaking agent", "bot_image_url": "https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg", "agent_config_id": "your-agent-config-id", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" }, "socket_connection_url": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge" }, "live_audio_required": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 24 } } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Create Bot — Full Payload import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "bot_message": "Hey Everyone :wave , I'm a speaking agent", "bot_image_url": "https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg", "agent_config_id": "your-agent-config-id", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" }, "socket_connection_url": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge" }, "live_audio_required": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 24 } } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Create Bot — Full Payload const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"bot_message":"Hey Everyone :wave , I\'m a speaking agent","bot_image_url":"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg","agent_config_id":"your-agent-config-id","custom_attributes":{"tag":"Meetstream","sample":"testing","user":"your-user-id"},"socket_connection_url":{"websocket_url":"wss://agent-meetstream-prd-main.meetstream.ai/bridge"},"live_audio_required":{"websocket_url":"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio"},"automatic_leave":{"waiting_room_timeout":100,"everyone_left_timeout":100,"voice_inactivity_timeout":100,"in_call_recording_timeout":14400,"recording_permission_denied_timeout":60},"recording_config":{"retention":{"type":"timed","hours":24}}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Create Bot — Full Payload package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone :wave , I'm a speaking agent\",\n \"bot_image_url\": \"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg\",\n \"agent_config_id\": \"your-agent-config-id\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n },\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n }\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Create Bot — Full Payload require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone :wave , I'm a speaking agent\",\n \"bot_image_url\": \"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg\",\n \"agent_config_id\": \"your-agent-config-id\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n },\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n }\n }\n}" response = http.request(request) puts response.read_body ``` ```java Create Bot — Full Payload import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone :wave , I'm a speaking agent\",\n \"bot_image_url\": \"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg\",\n \"agent_config_id\": \"your-agent-config-id\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n },\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n }\n }\n}") .asString(); ``` ```php Create Bot — Full Payload request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "bot_message": "Hey Everyone :wave , I\'m a speaking agent", "bot_image_url": "https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg", "agent_config_id": "your-agent-config-id", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" }, "socket_connection_url": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge" }, "live_audio_required": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 24 } } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Create Bot — Full Payload using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone :wave , I'm a speaking agent\",\n \"bot_image_url\": \"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg\",\n \"agent_config_id\": \"your-agent-config-id\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n },\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n }\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Create Bot — Full Payload import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "bot_message": "Hey Everyone :wave , I'm a speaking agent", "bot_image_url": "https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg", "agent_config_id": "your-agent-config-id", "custom_attributes": [ "tag": "Meetstream", "sample": "testing", "user": "your-user-id" ], "socket_connection_url": ["websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge"], "live_audio_required": ["websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio"], "automatic_leave": [ "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 ], "recording_config": ["retention": [ "type": "timed", "hours": 24 ]] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Basic Bot (Video Recording) **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Basic Bot (Video Recording) import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Basic Bot (Video Recording) const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Basic Bot (Video Recording) package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Basic Bot (Video Recording) require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true\n}" response = http.request(request) puts response.read_body ``` ```java Basic Bot (Video Recording) import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true\n}") .asString(); ``` ```php Basic Bot (Video Recording) request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Basic Bot (Video Recording) using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Basic Bot (Video Recording) import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Webhook **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": false, "callback_url": "https://your-server.com/webhook" } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Webhook import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": False, "callback_url": "https://your-server.com/webhook" } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Webhook const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":false,"callback_url":"https://your-server.com/webhook"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Webhook package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": false,\n \"callback_url\": \"https://your-server.com/webhook\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Webhook require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": false,\n \"callback_url\": \"https://your-server.com/webhook\"\n}" response = http.request(request) puts response.read_body ``` ```java Webhook import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": false,\n \"callback_url\": \"https://your-server.com/webhook\"\n}") .asString(); ``` ```php Webhook request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": false, "callback_url": "https://your-server.com/webhook" }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Webhook using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": false,\n \"callback_url\": \"https://your-server.com/webhook\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Webhook import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": false, "callback_url": "https://your-server.com/webhook" ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Bot with Custom Attributes **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Bot with Custom Attributes import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Bot with Custom Attributes const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"custom_attributes":{"tag":"Meetstream","sample":"testing","user":"your-user-id"}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Bot with Custom Attributes package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Bot with Custom Attributes require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n }\n}" response = http.request(request) puts response.read_body ``` ```java Bot with Custom Attributes import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n }\n}") .asString(); ``` ```php Bot with Custom Attributes request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Bot with Custom Attributes using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Bot with Custom Attributes import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "custom_attributes": [ "tag": "Meetstream", "sample": "testing", "user": "your-user-id" ] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Bot with Automatic Leave Config **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "automatic_leave": { "waiting_room_timeout": 600, "everyone_left_timeout": 300, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Bot with Automatic Leave Config import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "automatic_leave": { "waiting_room_timeout": 600, "everyone_left_timeout": 300, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Bot with Automatic Leave Config const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"automatic_leave":{"waiting_room_timeout":600,"everyone_left_timeout":300,"voice_inactivity_timeout":100,"in_call_recording_timeout":14400,"recording_permission_denied_timeout":60}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Bot with Automatic Leave Config package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"automatic_leave\": {\n \"waiting_room_timeout\": 600,\n \"everyone_left_timeout\": 300,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Bot with Automatic Leave Config require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"automatic_leave\": {\n \"waiting_room_timeout\": 600,\n \"everyone_left_timeout\": 300,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n }\n}" response = http.request(request) puts response.read_body ``` ```java Bot with Automatic Leave Config import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"automatic_leave\": {\n \"waiting_room_timeout\": 600,\n \"everyone_left_timeout\": 300,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n }\n}") .asString(); ``` ```php Bot with Automatic Leave Config request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "automatic_leave": { "waiting_room_timeout": 600, "everyone_left_timeout": 300, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Bot with Automatic Leave Config using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"automatic_leave\": {\n \"waiting_room_timeout\": 600,\n \"everyone_left_timeout\": 300,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Bot with Automatic Leave Config import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "automatic_leave": [ "waiting_room_timeout": 600, "everyone_left_timeout": 300, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 ] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Bot with Participants Notification **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "recording_config": { "realtime_endpoints": [ { "type": "webhook", "url": "https://your-server.com/webhook", "events": [ "participant_events.join", "participant_events.leave" ] } ] } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Bot with Participants Notification import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "recording_config": { "realtime_endpoints": [ { "type": "webhook", "url": "https://your-server.com/webhook", "events": ["participant_events.join", "participant_events.leave"] } ] } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Bot with Participants Notification const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"recording_config":{"realtime_endpoints":[{"type":"webhook","url":"https://your-server.com/webhook","events":["participant_events.join","participant_events.leave"]}]}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Bot with Participants Notification package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"recording_config\": {\n \"realtime_endpoints\": [\n {\n \"type\": \"webhook\",\n \"url\": \"https://your-server.com/webhook\",\n \"events\": [\n \"participant_events.join\",\n \"participant_events.leave\"\n ]\n }\n ]\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Bot with Participants Notification require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"recording_config\": {\n \"realtime_endpoints\": [\n {\n \"type\": \"webhook\",\n \"url\": \"https://your-server.com/webhook\",\n \"events\": [\n \"participant_events.join\",\n \"participant_events.leave\"\n ]\n }\n ]\n }\n}" response = http.request(request) puts response.read_body ``` ```java Bot with Participants Notification import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"recording_config\": {\n \"realtime_endpoints\": [\n {\n \"type\": \"webhook\",\n \"url\": \"https://your-server.com/webhook\",\n \"events\": [\n \"participant_events.join\",\n \"participant_events.leave\"\n ]\n }\n ]\n }\n}") .asString(); ``` ```php Bot with Participants Notification request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "recording_config": { "realtime_endpoints": [ { "type": "webhook", "url": "https://your-server.com/webhook", "events": [ "participant_events.join", "participant_events.leave" ] } ] } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Bot with Participants Notification using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"recording_config\": {\n \"realtime_endpoints\": [\n {\n \"type\": \"webhook\",\n \"url\": \"https://your-server.com/webhook\",\n \"events\": [\n \"participant_events.join\",\n \"participant_events.leave\"\n ]\n }\n ]\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Bot with Participants Notification import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "recording_config": ["realtime_endpoints": [ [ "type": "webhook", "url": "https://your-server.com/webhook", "events": ["participant_events.join", "participant_events.leave"] ] ]] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Bot with Auto Delete Config **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "callback_url": "https://your-server.com/webhook", "recording_config": { "retention": { "type": "timed", "hours": 24 } } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Bot with Auto Delete Config import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "callback_url": "https://your-server.com/webhook", "recording_config": { "retention": { "type": "timed", "hours": 24 } } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Bot with Auto Delete Config const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"callback_url":"https://your-server.com/webhook","recording_config":{"retention":{"type":"timed","hours":24}}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Bot with Auto Delete Config package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"callback_url\": \"https://your-server.com/webhook\",\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n }\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Bot with Auto Delete Config require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"callback_url\": \"https://your-server.com/webhook\",\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n }\n }\n}" response = http.request(request) puts response.read_body ``` ```java Bot with Auto Delete Config import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"callback_url\": \"https://your-server.com/webhook\",\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n }\n }\n}") .asString(); ``` ```php Bot with Auto Delete Config request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "callback_url": "https://your-server.com/webhook", "recording_config": { "retention": { "type": "timed", "hours": 24 } } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Bot with Auto Delete Config using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"callback_url\": \"https://your-server.com/webhook\",\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n }\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Bot with Auto Delete Config import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "callback_url": "https://your-server.com/webhook", "recording_config": ["retention": [ "type": "timed", "hours": 24 ]] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Schedule Bot **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "join_at": "2026-12-20T20:00:00+05:30" } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Schedule Bot import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "join_at": "2026-12-20T20:00:00+05:30" } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Schedule Bot const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"join_at":"2026-12-20T20:00:00+05:30"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Schedule Bot package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"join_at\": \"2026-12-20T20:00:00+05:30\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Schedule Bot require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"join_at\": \"2026-12-20T20:00:00+05:30\"\n}" response = http.request(request) puts response.read_body ``` ```java Schedule Bot import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"join_at\": \"2026-12-20T20:00:00+05:30\"\n}") .asString(); ``` ```php Schedule Bot request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "join_at": "2026-12-20T20:00:00+05:30" }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Schedule Bot using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"join_at\": \"2026-12-20T20:00:00+05:30\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Schedule Bot import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "join_at": "2026-12-20T20:00:00+05:30" ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Bot with Image **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "bot_image_url": "https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png" } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Bot with Image import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "bot_image_url": "https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png" } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Bot with Image const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"bot_image_url":"https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Bot with Image package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_image_url\": \"https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Bot with Image require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_image_url\": \"https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png\"\n}" response = http.request(request) puts response.read_body ``` ```java Bot with Image import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_image_url\": \"https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png\"\n}") .asString(); ``` ```php Bot with Image request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "bot_image_url": "https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png" }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Bot with Image using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_image_url\": \"https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Bot with Image import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "bot_image_url": "https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png" ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Bot with Initial Message in Chat **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "bot_message": "Hey Everyone :wave:, I'm a speaking agent" } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Bot with Initial Message in Chat import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "bot_message": "Hey Everyone :wave:, I'm a speaking agent" } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Bot with Initial Message in Chat const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"bot_message":"Hey Everyone :wave:, I\'m a speaking agent"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Bot with Initial Message in Chat package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone :wave:, I'm a speaking agent\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Bot with Initial Message in Chat require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone :wave:, I'm a speaking agent\"\n}" response = http.request(request) puts response.read_body ``` ```java Bot with Initial Message in Chat import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone :wave:, I'm a speaking agent\"\n}") .asString(); ``` ```php Bot with Initial Message in Chat request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "bot_message": "Hey Everyone :wave:, I\'m a speaking agent" }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Bot with Initial Message in Chat using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone :wave:, I'm a speaking agent\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Bot with Initial Message in Chat import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "bot_message": "Hey Everyone :wave:, I'm a speaking agent" ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Transcription — Deepgram **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "recording_config": { "transcript": { "provider": { "deepgram": { "model": "nova-3", "language": "en", "punctuate": true, "smart_format": true, "diarize": true, "paragraphs": true, "numerals": true, "filler_words": false, "utterances": true, "utt_split": 0.8, "detect_language": false, "keywords": [ "MeetStream", "recording", "transcript" ], "search": [ "MeetStream", "recording" ], "tag": [ "custom" ] } } } } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Transcription — Deepgram import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "recording_config": { "transcript": { "provider": { "deepgram": { "model": "nova-3", "language": "en", "punctuate": True, "smart_format": True, "diarize": True, "paragraphs": True, "numerals": True, "filler_words": False, "utterances": True, "utt_split": 0.8, "detect_language": False, "keywords": ["MeetStream", "recording", "transcript"], "search": ["MeetStream", "recording"], "tag": ["custom"] } } } } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Transcription — Deepgram const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"recording_config":{"transcript":{"provider":{"deepgram":{"model":"nova-3","language":"en","punctuate":true,"smart_format":true,"diarize":true,"paragraphs":true,"numerals":true,"filler_words":false,"utterances":true,"utt_split":0.8,"detect_language":false,"keywords":["MeetStream","recording","transcript"],"search":["MeetStream","recording"],"tag":["custom"]}}}}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Transcription — Deepgram package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"deepgram\": {\n \"model\": \"nova-3\",\n \"language\": \"en\",\n \"punctuate\": true,\n \"smart_format\": true,\n \"diarize\": true,\n \"paragraphs\": true,\n \"numerals\": true,\n \"filler_words\": false,\n \"utterances\": true,\n \"utt_split\": 0.8,\n \"detect_language\": false,\n \"keywords\": [\n \"MeetStream\",\n \"recording\",\n \"transcript\"\n ],\n \"search\": [\n \"MeetStream\",\n \"recording\"\n ],\n \"tag\": [\n \"custom\"\n ]\n }\n }\n }\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Transcription — Deepgram require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"deepgram\": {\n \"model\": \"nova-3\",\n \"language\": \"en\",\n \"punctuate\": true,\n \"smart_format\": true,\n \"diarize\": true,\n \"paragraphs\": true,\n \"numerals\": true,\n \"filler_words\": false,\n \"utterances\": true,\n \"utt_split\": 0.8,\n \"detect_language\": false,\n \"keywords\": [\n \"MeetStream\",\n \"recording\",\n \"transcript\"\n ],\n \"search\": [\n \"MeetStream\",\n \"recording\"\n ],\n \"tag\": [\n \"custom\"\n ]\n }\n }\n }\n }\n}" response = http.request(request) puts response.read_body ``` ```java Transcription — Deepgram import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"deepgram\": {\n \"model\": \"nova-3\",\n \"language\": \"en\",\n \"punctuate\": true,\n \"smart_format\": true,\n \"diarize\": true,\n \"paragraphs\": true,\n \"numerals\": true,\n \"filler_words\": false,\n \"utterances\": true,\n \"utt_split\": 0.8,\n \"detect_language\": false,\n \"keywords\": [\n \"MeetStream\",\n \"recording\",\n \"transcript\"\n ],\n \"search\": [\n \"MeetStream\",\n \"recording\"\n ],\n \"tag\": [\n \"custom\"\n ]\n }\n }\n }\n }\n}") .asString(); ``` ```php Transcription — Deepgram request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "recording_config": { "transcript": { "provider": { "deepgram": { "model": "nova-3", "language": "en", "punctuate": true, "smart_format": true, "diarize": true, "paragraphs": true, "numerals": true, "filler_words": false, "utterances": true, "utt_split": 0.8, "detect_language": false, "keywords": [ "MeetStream", "recording", "transcript" ], "search": [ "MeetStream", "recording" ], "tag": [ "custom" ] } } } } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Transcription — Deepgram using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"deepgram\": {\n \"model\": \"nova-3\",\n \"language\": \"en\",\n \"punctuate\": true,\n \"smart_format\": true,\n \"diarize\": true,\n \"paragraphs\": true,\n \"numerals\": true,\n \"filler_words\": false,\n \"utterances\": true,\n \"utt_split\": 0.8,\n \"detect_language\": false,\n \"keywords\": [\n \"MeetStream\",\n \"recording\",\n \"transcript\"\n ],\n \"search\": [\n \"MeetStream\",\n \"recording\"\n ],\n \"tag\": [\n \"custom\"\n ]\n }\n }\n }\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Transcription — Deepgram import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "recording_config": ["transcript": ["provider": ["deepgram": [ "model": "nova-3", "language": "en", "punctuate": true, "smart_format": true, "diarize": true, "paragraphs": true, "numerals": true, "filler_words": false, "utterances": true, "utt_split": 0.8, "detect_language": false, "keywords": ["MeetStream", "recording", "transcript"], "search": ["MeetStream", "recording"], "tag": ["custom"] ]]]] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Transcription — AssemblyAI **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "recording_config": { "transcript": { "provider": { "assemblyai": { "speech_models": [ "universal-2" ], "language_code": "en_us", "speaker_labels": true, "punctuate": true, "format_text": true, "filter_profanity": false, "redact_pii": false, "auto_chapters": false, "entity_detection": false, "keyterms_prompt": [ "MeetStream" ] } } } } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Transcription — AssemblyAI import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "recording_config": { "transcript": { "provider": { "assemblyai": { "speech_models": ["universal-2"], "language_code": "en_us", "speaker_labels": True, "punctuate": True, "format_text": True, "filter_profanity": False, "redact_pii": False, "auto_chapters": False, "entity_detection": False, "keyterms_prompt": ["MeetStream"] } } } } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Transcription — AssemblyAI const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"recording_config":{"transcript":{"provider":{"assemblyai":{"speech_models":["universal-2"],"language_code":"en_us","speaker_labels":true,"punctuate":true,"format_text":true,"filter_profanity":false,"redact_pii":false,"auto_chapters":false,"entity_detection":false,"keyterms_prompt":["MeetStream"]}}}}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Transcription — AssemblyAI package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"assemblyai\": {\n \"speech_models\": [\n \"universal-2\"\n ],\n \"language_code\": \"en_us\",\n \"speaker_labels\": true,\n \"punctuate\": true,\n \"format_text\": true,\n \"filter_profanity\": false,\n \"redact_pii\": false,\n \"auto_chapters\": false,\n \"entity_detection\": false,\n \"keyterms_prompt\": [\n \"MeetStream\"\n ]\n }\n }\n }\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Transcription — AssemblyAI require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"assemblyai\": {\n \"speech_models\": [\n \"universal-2\"\n ],\n \"language_code\": \"en_us\",\n \"speaker_labels\": true,\n \"punctuate\": true,\n \"format_text\": true,\n \"filter_profanity\": false,\n \"redact_pii\": false,\n \"auto_chapters\": false,\n \"entity_detection\": false,\n \"keyterms_prompt\": [\n \"MeetStream\"\n ]\n }\n }\n }\n }\n}" response = http.request(request) puts response.read_body ``` ```java Transcription — AssemblyAI import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"assemblyai\": {\n \"speech_models\": [\n \"universal-2\"\n ],\n \"language_code\": \"en_us\",\n \"speaker_labels\": true,\n \"punctuate\": true,\n \"format_text\": true,\n \"filter_profanity\": false,\n \"redact_pii\": false,\n \"auto_chapters\": false,\n \"entity_detection\": false,\n \"keyterms_prompt\": [\n \"MeetStream\"\n ]\n }\n }\n }\n }\n}") .asString(); ``` ```php Transcription — AssemblyAI request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "recording_config": { "transcript": { "provider": { "assemblyai": { "speech_models": [ "universal-2" ], "language_code": "en_us", "speaker_labels": true, "punctuate": true, "format_text": true, "filter_profanity": false, "redact_pii": false, "auto_chapters": false, "entity_detection": false, "keyterms_prompt": [ "MeetStream" ] } } } } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Transcription — AssemblyAI using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"assemblyai\": {\n \"speech_models\": [\n \"universal-2\"\n ],\n \"language_code\": \"en_us\",\n \"speaker_labels\": true,\n \"punctuate\": true,\n \"format_text\": true,\n \"filter_profanity\": false,\n \"redact_pii\": false,\n \"auto_chapters\": false,\n \"entity_detection\": false,\n \"keyterms_prompt\": [\n \"MeetStream\"\n ]\n }\n }\n }\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Transcription — AssemblyAI import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "recording_config": ["transcript": ["provider": ["assemblyai": [ "speech_models": ["universal-2"], "language_code": "en_us", "speaker_labels": true, "punctuate": true, "format_text": true, "filter_profanity": false, "redact_pii": false, "auto_chapters": false, "entity_detection": false, "keyterms_prompt": ["MeetStream"] ]]]] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Transcription — Jigsaw **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "bot_message": "Hey Everyone, I'm Checking Jigsawstack transcript", "bot_image_url": "https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 24 }, "transcript": { "provider": { "jigsawstack": { "language": "auto", "translate": false, "by_speaker": true } } } } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Transcription — Jigsaw import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "bot_message": "Hey Everyone, I'm Checking Jigsawstack transcript", "bot_image_url": "https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 24 }, "transcript": { "provider": { "jigsawstack": { "language": "auto", "translate": False, "by_speaker": True } } } } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Transcription — Jigsaw const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"bot_message":"Hey Everyone, I\'m Checking Jigsawstack transcript","bot_image_url":"https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png","custom_attributes":{"tag":"Meetstream","sample":"testing","user":"your-user-id"},"automatic_leave":{"waiting_room_timeout":100,"everyone_left_timeout":100,"voice_inactivity_timeout":100,"in_call_recording_timeout":14400,"recording_permission_denied_timeout":60},"recording_config":{"retention":{"type":"timed","hours":24},"transcript":{"provider":{"jigsawstack":{"language":"auto","translate":false,"by_speaker":true}}}}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Transcription — Jigsaw package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone, I'm Checking Jigsawstack transcript\",\n \"bot_image_url\": \"https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n },\n \"transcript\": {\n \"provider\": {\n \"jigsawstack\": {\n \"language\": \"auto\",\n \"translate\": false,\n \"by_speaker\": true\n }\n }\n }\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Transcription — Jigsaw require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone, I'm Checking Jigsawstack transcript\",\n \"bot_image_url\": \"https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n },\n \"transcript\": {\n \"provider\": {\n \"jigsawstack\": {\n \"language\": \"auto\",\n \"translate\": false,\n \"by_speaker\": true\n }\n }\n }\n }\n}" response = http.request(request) puts response.read_body ``` ```java Transcription — Jigsaw import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone, I'm Checking Jigsawstack transcript\",\n \"bot_image_url\": \"https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n },\n \"transcript\": {\n \"provider\": {\n \"jigsawstack\": {\n \"language\": \"auto\",\n \"translate\": false,\n \"by_speaker\": true\n }\n }\n }\n }\n}") .asString(); ``` ```php Transcription — Jigsaw request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "bot_message": "Hey Everyone, I\'m Checking Jigsawstack transcript", "bot_image_url": "https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 24 }, "transcript": { "provider": { "jigsawstack": { "language": "auto", "translate": false, "by_speaker": true } } } } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Transcription — Jigsaw using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone, I'm Checking Jigsawstack transcript\",\n \"bot_image_url\": \"https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n },\n \"transcript\": {\n \"provider\": {\n \"jigsawstack\": {\n \"language\": \"auto\",\n \"translate\": false,\n \"by_speaker\": true\n }\n }\n }\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Transcription — Jigsaw import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "bot_message": "Hey Everyone, I'm Checking Jigsawstack transcript", "bot_image_url": "https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png", "custom_attributes": [ "tag": "Meetstream", "sample": "testing", "user": "your-user-id" ], "automatic_leave": [ "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 ], "recording_config": [ "retention": [ "type": "timed", "hours": 24 ], "transcript": ["provider": ["jigsawstack": [ "language": "auto", "translate": false, "by_speaker": true ]]] ] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Transcription — MeetStream **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "MeetStream Agent", "video_required": true, "bot_message": "Hey Everyone, I'm Checking Jigsawstack transcript", "bot_image_url": "https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your_user_id" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 24 }, "transcript": { "provider": { "meetstream": { "language": "auto", "translate": false } } } } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Transcription — MeetStream import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "MeetStream Agent", "video_required": True, "bot_message": "Hey Everyone, I'm Checking Jigsawstack transcript", "bot_image_url": "https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your_user_id" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 24 }, "transcript": { "provider": { "meetstream": { "language": "auto", "translate": False } } } } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Transcription — MeetStream const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"MeetStream Agent","video_required":true,"bot_message":"Hey Everyone, I\'m Checking Jigsawstack transcript","bot_image_url":"https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png","custom_attributes":{"tag":"Meetstream","sample":"testing","user":"your_user_id"},"automatic_leave":{"waiting_room_timeout":100,"everyone_left_timeout":100,"voice_inactivity_timeout":100,"in_call_recording_timeout":14400,"recording_permission_denied_timeout":60},"recording_config":{"retention":{"type":"timed","hours":24},"transcript":{"provider":{"meetstream":{"language":"auto","translate":false}}}}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Transcription — MeetStream package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"MeetStream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone, I'm Checking Jigsawstack transcript\",\n \"bot_image_url\": \"https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your_user_id\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n },\n \"transcript\": {\n \"provider\": {\n \"meetstream\": {\n \"language\": \"auto\",\n \"translate\": false\n }\n }\n }\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Transcription — MeetStream require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"MeetStream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone, I'm Checking Jigsawstack transcript\",\n \"bot_image_url\": \"https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your_user_id\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n },\n \"transcript\": {\n \"provider\": {\n \"meetstream\": {\n \"language\": \"auto\",\n \"translate\": false\n }\n }\n }\n }\n}" response = http.request(request) puts response.read_body ``` ```java Transcription — MeetStream import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"MeetStream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone, I'm Checking Jigsawstack transcript\",\n \"bot_image_url\": \"https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your_user_id\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n },\n \"transcript\": {\n \"provider\": {\n \"meetstream\": {\n \"language\": \"auto\",\n \"translate\": false\n }\n }\n }\n }\n}") .asString(); ``` ```php Transcription — MeetStream request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "MeetStream Agent", "video_required": true, "bot_message": "Hey Everyone, I\'m Checking Jigsawstack transcript", "bot_image_url": "https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your_user_id" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 24 }, "transcript": { "provider": { "meetstream": { "language": "auto", "translate": false } } } } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Transcription — MeetStream using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"MeetStream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone, I'm Checking Jigsawstack transcript\",\n \"bot_image_url\": \"https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your_user_id\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n },\n \"transcript\": {\n \"provider\": {\n \"meetstream\": {\n \"language\": \"auto\",\n \"translate\": false\n }\n }\n }\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Transcription — MeetStream import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "MeetStream Agent", "video_required": true, "bot_message": "Hey Everyone, I'm Checking Jigsawstack transcript", "bot_image_url": "https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png", "custom_attributes": [ "tag": "Meetstream", "sample": "testing", "user": "your_user_id" ], "automatic_leave": [ "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 ], "recording_config": [ "retention": [ "type": "timed", "hours": 24 ], "transcript": ["provider": ["meetstream": [ "language": "auto", "translate": false ]]] ] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Transcription — Sarvam **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "callback_url": "https://your-server.com/webhook", "recording_config": { "transcript": { "provider": { "sarvam": { "model": "saaras:v3", "language_code": "en-IN", "mode": "transcribe", "with_diarization": true } } } } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Transcription — Sarvam import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "callback_url": "https://your-server.com/webhook", "recording_config": { "transcript": { "provider": { "sarvam": { "model": "saaras:v3", "language_code": "en-IN", "mode": "transcribe", "with_diarization": True } } } } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Transcription — Sarvam const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"callback_url":"https://your-server.com/webhook","recording_config":{"transcript":{"provider":{"sarvam":{"model":"saaras:v3","language_code":"en-IN","mode":"transcribe","with_diarization":true}}}}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Transcription — Sarvam package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"callback_url\": \"https://your-server.com/webhook\",\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"sarvam\": {\n \"model\": \"saaras:v3\",\n \"language_code\": \"en-IN\",\n \"mode\": \"transcribe\",\n \"with_diarization\": true\n }\n }\n }\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Transcription — Sarvam require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"callback_url\": \"https://your-server.com/webhook\",\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"sarvam\": {\n \"model\": \"saaras:v3\",\n \"language_code\": \"en-IN\",\n \"mode\": \"transcribe\",\n \"with_diarization\": true\n }\n }\n }\n }\n}" response = http.request(request) puts response.read_body ``` ```java Transcription — Sarvam import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"callback_url\": \"https://your-server.com/webhook\",\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"sarvam\": {\n \"model\": \"saaras:v3\",\n \"language_code\": \"en-IN\",\n \"mode\": \"transcribe\",\n \"with_diarization\": true\n }\n }\n }\n }\n}") .asString(); ``` ```php Transcription — Sarvam request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "callback_url": "https://your-server.com/webhook", "recording_config": { "transcript": { "provider": { "sarvam": { "model": "saaras:v3", "language_code": "en-IN", "mode": "transcribe", "with_diarization": true } } } } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Transcription — Sarvam using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"callback_url\": \"https://your-server.com/webhook\",\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"sarvam\": {\n \"model\": \"saaras:v3\",\n \"language_code\": \"en-IN\",\n \"mode\": \"transcribe\",\n \"with_diarization\": true\n }\n }\n }\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Transcription — Sarvam import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "callback_url": "https://your-server.com/webhook", "recording_config": ["transcript": ["provider": ["sarvam": [ "model": "saaras:v3", "language_code": "en-IN", "mode": "transcribe", "with_diarization": true ]]]] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Transcription — Captions **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "live_transcription_required": { "webhook_url": "https://your-server.com/webhook" }, "recording_config": { "transcript": { "provider": { "meeting_captions": {} } } } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Transcription — Captions import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "live_transcription_required": { "webhook_url": "https://your-server.com/webhook" }, "recording_config": { "transcript": { "provider": { "meeting_captions": {} } } } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Transcription — Captions const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"live_transcription_required":{"webhook_url":"https://your-server.com/webhook"},"recording_config":{"transcript":{"provider":{"meeting_captions":{}}}}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Transcription — Captions package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"live_transcription_required\": {\n \"webhook_url\": \"https://your-server.com/webhook\"\n },\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"meeting_captions\": {}\n }\n }\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Transcription — Captions require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"live_transcription_required\": {\n \"webhook_url\": \"https://your-server.com/webhook\"\n },\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"meeting_captions\": {}\n }\n }\n }\n}" response = http.request(request) puts response.read_body ``` ```java Transcription — Captions import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"live_transcription_required\": {\n \"webhook_url\": \"https://your-server.com/webhook\"\n },\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"meeting_captions\": {}\n }\n }\n }\n}") .asString(); ``` ```php Transcription — Captions request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "live_transcription_required": { "webhook_url": "https://your-server.com/webhook" }, "recording_config": { "transcript": { "provider": { "meeting_captions": {} } } } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Transcription — Captions using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"live_transcription_required\": {\n \"webhook_url\": \"https://your-server.com/webhook\"\n },\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"meeting_captions\": {}\n }\n }\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Transcription — Captions import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "live_transcription_required": ["webhook_url": "https://your-server.com/webhook"], "recording_config": ["transcript": ["provider": ["meeting_captions": []]]] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Transcription — AssemblyAI Streaming **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "live_transcription_required": { "webhook_url": "https://your-server.com/webhook" }, "recording_config": { "transcript": { "provider": { "assemblyai_streaming": { "speech_model": "universal-streaming-english", "transcription_mode": "raw", "sample_rate": 48000, "format_turns": false, "encoding": "pcm_s16le", "vad_threshold": "0.4", "end_of_turn_confidence_threshold": "0.4", "inactivity_timeout": 300, "min_end_of_turn_silence_when_confident": "400", "max_turn_silence": "1280" } } } } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Transcription — AssemblyAI Streaming import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "live_transcription_required": { "webhook_url": "https://your-server.com/webhook" }, "recording_config": { "transcript": { "provider": { "assemblyai_streaming": { "speech_model": "universal-streaming-english", "transcription_mode": "raw", "sample_rate": 48000, "format_turns": False, "encoding": "pcm_s16le", "vad_threshold": "0.4", "end_of_turn_confidence_threshold": "0.4", "inactivity_timeout": 300, "min_end_of_turn_silence_when_confident": "400", "max_turn_silence": "1280" } } } } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Transcription — AssemblyAI Streaming const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"live_transcription_required":{"webhook_url":"https://your-server.com/webhook"},"recording_config":{"transcript":{"provider":{"assemblyai_streaming":{"speech_model":"universal-streaming-english","transcription_mode":"raw","sample_rate":48000,"format_turns":false,"encoding":"pcm_s16le","vad_threshold":"0.4","end_of_turn_confidence_threshold":"0.4","inactivity_timeout":300,"min_end_of_turn_silence_when_confident":"400","max_turn_silence":"1280"}}}}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Transcription — AssemblyAI Streaming package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"live_transcription_required\": {\n \"webhook_url\": \"https://your-server.com/webhook\"\n },\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"assemblyai_streaming\": {\n \"speech_model\": \"universal-streaming-english\",\n \"transcription_mode\": \"raw\",\n \"sample_rate\": 48000,\n \"format_turns\": false,\n \"encoding\": \"pcm_s16le\",\n \"vad_threshold\": \"0.4\",\n \"end_of_turn_confidence_threshold\": \"0.4\",\n \"inactivity_timeout\": 300,\n \"min_end_of_turn_silence_when_confident\": \"400\",\n \"max_turn_silence\": \"1280\"\n }\n }\n }\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Transcription — AssemblyAI Streaming require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"live_transcription_required\": {\n \"webhook_url\": \"https://your-server.com/webhook\"\n },\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"assemblyai_streaming\": {\n \"speech_model\": \"universal-streaming-english\",\n \"transcription_mode\": \"raw\",\n \"sample_rate\": 48000,\n \"format_turns\": false,\n \"encoding\": \"pcm_s16le\",\n \"vad_threshold\": \"0.4\",\n \"end_of_turn_confidence_threshold\": \"0.4\",\n \"inactivity_timeout\": 300,\n \"min_end_of_turn_silence_when_confident\": \"400\",\n \"max_turn_silence\": \"1280\"\n }\n }\n }\n }\n}" response = http.request(request) puts response.read_body ``` ```java Transcription — AssemblyAI Streaming import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"live_transcription_required\": {\n \"webhook_url\": \"https://your-server.com/webhook\"\n },\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"assemblyai_streaming\": {\n \"speech_model\": \"universal-streaming-english\",\n \"transcription_mode\": \"raw\",\n \"sample_rate\": 48000,\n \"format_turns\": false,\n \"encoding\": \"pcm_s16le\",\n \"vad_threshold\": \"0.4\",\n \"end_of_turn_confidence_threshold\": \"0.4\",\n \"inactivity_timeout\": 300,\n \"min_end_of_turn_silence_when_confident\": \"400\",\n \"max_turn_silence\": \"1280\"\n }\n }\n }\n }\n}") .asString(); ``` ```php Transcription — AssemblyAI Streaming request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "live_transcription_required": { "webhook_url": "https://your-server.com/webhook" }, "recording_config": { "transcript": { "provider": { "assemblyai_streaming": { "speech_model": "universal-streaming-english", "transcription_mode": "raw", "sample_rate": 48000, "format_turns": false, "encoding": "pcm_s16le", "vad_threshold": "0.4", "end_of_turn_confidence_threshold": "0.4", "inactivity_timeout": 300, "min_end_of_turn_silence_when_confident": "400", "max_turn_silence": "1280" } } } } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Transcription — AssemblyAI Streaming using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"live_transcription_required\": {\n \"webhook_url\": \"https://your-server.com/webhook\"\n },\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"assemblyai_streaming\": {\n \"speech_model\": \"universal-streaming-english\",\n \"transcription_mode\": \"raw\",\n \"sample_rate\": 48000,\n \"format_turns\": false,\n \"encoding\": \"pcm_s16le\",\n \"vad_threshold\": \"0.4\",\n \"end_of_turn_confidence_threshold\": \"0.4\",\n \"inactivity_timeout\": 300,\n \"min_end_of_turn_silence_when_confident\": \"400\",\n \"max_turn_silence\": \"1280\"\n }\n }\n }\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Transcription — AssemblyAI Streaming import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "live_transcription_required": ["webhook_url": "https://your-server.com/webhook"], "recording_config": ["transcript": ["provider": ["assemblyai_streaming": [ "speech_model": "universal-streaming-english", "transcription_mode": "raw", "sample_rate": 48000, "format_turns": false, "encoding": "pcm_s16le", "vad_threshold": "0.4", "end_of_turn_confidence_threshold": "0.4", "inactivity_timeout": 300, "min_end_of_turn_silence_when_confident": "400", "max_turn_silence": "1280" ]]]] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Transcription — Deepgram Streaming **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "live_transcription_required": { "webhook_url": "https://your-server.com/webhook" }, "recording_config": { "transcript": { "provider": { "deepgram_streaming": { "model": "nova-2", "transcription_mode": "sentence", "language": "en", "punctuate": true, "smart_format": true, "endpointing": 300, "vad_events": true, "utterance_end_ms": 1000, "encoding": "linear16", "channels": 1 } } } } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Transcription — Deepgram Streaming import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "live_transcription_required": { "webhook_url": "https://your-server.com/webhook" }, "recording_config": { "transcript": { "provider": { "deepgram_streaming": { "model": "nova-2", "transcription_mode": "sentence", "language": "en", "punctuate": True, "smart_format": True, "endpointing": 300, "vad_events": True, "utterance_end_ms": 1000, "encoding": "linear16", "channels": 1 } } } } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Transcription — Deepgram Streaming const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"live_transcription_required":{"webhook_url":"https://your-server.com/webhook"},"recording_config":{"transcript":{"provider":{"deepgram_streaming":{"model":"nova-2","transcription_mode":"sentence","language":"en","punctuate":true,"smart_format":true,"endpointing":300,"vad_events":true,"utterance_end_ms":1000,"encoding":"linear16","channels":1}}}}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Transcription — Deepgram Streaming package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"live_transcription_required\": {\n \"webhook_url\": \"https://your-server.com/webhook\"\n },\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"deepgram_streaming\": {\n \"model\": \"nova-2\",\n \"transcription_mode\": \"sentence\",\n \"language\": \"en\",\n \"punctuate\": true,\n \"smart_format\": true,\n \"endpointing\": 300,\n \"vad_events\": true,\n \"utterance_end_ms\": 1000,\n \"encoding\": \"linear16\",\n \"channels\": 1\n }\n }\n }\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Transcription — Deepgram Streaming require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"live_transcription_required\": {\n \"webhook_url\": \"https://your-server.com/webhook\"\n },\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"deepgram_streaming\": {\n \"model\": \"nova-2\",\n \"transcription_mode\": \"sentence\",\n \"language\": \"en\",\n \"punctuate\": true,\n \"smart_format\": true,\n \"endpointing\": 300,\n \"vad_events\": true,\n \"utterance_end_ms\": 1000,\n \"encoding\": \"linear16\",\n \"channels\": 1\n }\n }\n }\n }\n}" response = http.request(request) puts response.read_body ``` ```java Transcription — Deepgram Streaming import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"live_transcription_required\": {\n \"webhook_url\": \"https://your-server.com/webhook\"\n },\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"deepgram_streaming\": {\n \"model\": \"nova-2\",\n \"transcription_mode\": \"sentence\",\n \"language\": \"en\",\n \"punctuate\": true,\n \"smart_format\": true,\n \"endpointing\": 300,\n \"vad_events\": true,\n \"utterance_end_ms\": 1000,\n \"encoding\": \"linear16\",\n \"channels\": 1\n }\n }\n }\n }\n}") .asString(); ``` ```php Transcription — Deepgram Streaming request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "live_transcription_required": { "webhook_url": "https://your-server.com/webhook" }, "recording_config": { "transcript": { "provider": { "deepgram_streaming": { "model": "nova-2", "transcription_mode": "sentence", "language": "en", "punctuate": true, "smart_format": true, "endpointing": 300, "vad_events": true, "utterance_end_ms": 1000, "encoding": "linear16", "channels": 1 } } } } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Transcription — Deepgram Streaming using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"live_transcription_required\": {\n \"webhook_url\": \"https://your-server.com/webhook\"\n },\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"deepgram_streaming\": {\n \"model\": \"nova-2\",\n \"transcription_mode\": \"sentence\",\n \"language\": \"en\",\n \"punctuate\": true,\n \"smart_format\": true,\n \"endpointing\": 300,\n \"vad_events\": true,\n \"utterance_end_ms\": 1000,\n \"encoding\": \"linear16\",\n \"channels\": 1\n }\n }\n }\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Transcription — Deepgram Streaming import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "live_transcription_required": ["webhook_url": "https://your-server.com/webhook"], "recording_config": ["transcript": ["provider": ["deepgram_streaming": [ "model": "nova-2", "transcription_mode": "sentence", "language": "en", "punctuate": true, "smart_format": true, "endpointing": 300, "vad_events": true, "utterance_end_ms": 1000, "encoding": "linear16", "channels": 1 ]]]] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Bot with Audio Streaming (Two Way) **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "socket_connection_url": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge" }, "live_audio_required": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio" } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Bot with Audio Streaming (Two Way) import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "socket_connection_url": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge" }, "live_audio_required": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio" } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Bot with Audio Streaming (Two Way) const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"socket_connection_url":{"websocket_url":"wss://agent-meetstream-prd-main.meetstream.ai/bridge"},"live_audio_required":{"websocket_url":"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio"}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Bot with Audio Streaming (Two Way) package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n },\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Bot with Audio Streaming (Two Way) require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n },\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n }\n}" response = http.request(request) puts response.read_body ``` ```java Bot with Audio Streaming (Two Way) import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n },\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n }\n}") .asString(); ``` ```php Bot with Audio Streaming (Two Way) request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "socket_connection_url": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge" }, "live_audio_required": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio" } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Bot with Audio Streaming (Two Way) using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n },\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Bot with Audio Streaming (Two Way) import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "socket_connection_url": ["websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge"], "live_audio_required": ["websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio"] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Bot with Dynamic Messaging **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Grok Agent", "video_required": true, "socket_connection_url": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge" } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Bot with Dynamic Messaging import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Grok Agent", "video_required": True, "socket_connection_url": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge" } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Bot with Dynamic Messaging const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Grok Agent","video_required":true,"socket_connection_url":{"websocket_url":"wss://agent-meetstream-prd-main.meetstream.ai/bridge"}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Bot with Dynamic Messaging package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Grok Agent\",\n \"video_required\": true,\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Bot with Dynamic Messaging require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Grok Agent\",\n \"video_required\": true,\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n }\n}" response = http.request(request) puts response.read_body ``` ```java Bot with Dynamic Messaging import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Grok Agent\",\n \"video_required\": true,\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n }\n}") .asString(); ``` ```php Bot with Dynamic Messaging request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Grok Agent", "video_required": true, "socket_connection_url": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge" } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Bot with Dynamic Messaging using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Grok Agent\",\n \"video_required\": true,\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Bot with Dynamic Messaging import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Grok Agent", "video_required": true, "socket_connection_url": ["websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge"] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Bot with Live Audio Streaming **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Grok Agent", "video_required": true, "live_audio_required": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio" } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Bot with Live Audio Streaming import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Grok Agent", "video_required": True, "live_audio_required": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio" } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Bot with Live Audio Streaming const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Grok Agent","video_required":true,"live_audio_required":{"websocket_url":"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio"}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Bot with Live Audio Streaming package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Grok Agent\",\n \"video_required\": true,\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Bot with Live Audio Streaming require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Grok Agent\",\n \"video_required\": true,\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n }\n}" response = http.request(request) puts response.read_body ``` ```java Bot with Live Audio Streaming import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Grok Agent\",\n \"video_required\": true,\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n }\n}") .asString(); ``` ```php Bot with Live Audio Streaming request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Grok Agent", "video_required": true, "live_audio_required": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio" } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Bot with Live Audio Streaming using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Grok Agent\",\n \"video_required\": true,\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Bot with Live Audio Streaming import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Grok Agent", "video_required": true, "live_audio_required": ["websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio"] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Video Streaming **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "bot_message": "Hey Everyone :wave:, I'm a speaking agent", "bot_image_url": "https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png", "live_audio_required": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio" }, "live_video_required": { "websocket_url": "wss://your-server.com/video-stream" } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Video Streaming import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "bot_message": "Hey Everyone :wave:, I'm a speaking agent", "bot_image_url": "https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png", "live_audio_required": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio" }, "live_video_required": { "websocket_url": "wss://your-server.com/video-stream" } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Video Streaming const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"bot_message":"Hey Everyone :wave:, I\'m a speaking agent","bot_image_url":"https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png","live_audio_required":{"websocket_url":"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio"},"live_video_required":{"websocket_url":"wss://your-server.com/video-stream"}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Video Streaming package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone :wave:, I'm a speaking agent\",\n \"bot_image_url\": \"https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png\",\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n },\n \"live_video_required\": {\n \"websocket_url\": \"wss://your-server.com/video-stream\"\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Video Streaming require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone :wave:, I'm a speaking agent\",\n \"bot_image_url\": \"https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png\",\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n },\n \"live_video_required\": {\n \"websocket_url\": \"wss://your-server.com/video-stream\"\n }\n}" response = http.request(request) puts response.read_body ``` ```java Video Streaming import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone :wave:, I'm a speaking agent\",\n \"bot_image_url\": \"https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png\",\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n },\n \"live_video_required\": {\n \"websocket_url\": \"wss://your-server.com/video-stream\"\n }\n}") .asString(); ``` ```php Video Streaming request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "bot_message": "Hey Everyone :wave:, I\'m a speaking agent", "bot_image_url": "https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png", "live_audio_required": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio" }, "live_video_required": { "websocket_url": "wss://your-server.com/video-stream" } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Video Streaming using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone :wave:, I'm a speaking agent\",\n \"bot_image_url\": \"https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png\",\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n },\n \"live_video_required\": {\n \"websocket_url\": \"wss://your-server.com/video-stream\"\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Video Streaming import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "bot_message": "Hey Everyone :wave:, I'm a speaking agent", "bot_image_url": "https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png", "live_audio_required": ["websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio"], "live_video_required": ["websocket_url": "wss://your-server.com/video-stream"] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### MIA (Meetstream Infrastructure Agent) **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "bot_message": "Hey Everyone :wave , I'm a speaking agent", "bot_image_url": "https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg", "agent_config_id": "your-agent-config-id", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" }, "socket_connection_url": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge" }, "live_audio_required": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 24 } } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python MIA (Meetstream Infrastructure Agent) import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "bot_message": "Hey Everyone :wave , I'm a speaking agent", "bot_image_url": "https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg", "agent_config_id": "your-agent-config-id", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" }, "socket_connection_url": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge" }, "live_audio_required": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 24 } } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript MIA (Meetstream Infrastructure Agent) const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"bot_message":"Hey Everyone :wave , I\'m a speaking agent","bot_image_url":"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg","agent_config_id":"your-agent-config-id","custom_attributes":{"tag":"Meetstream","sample":"testing","user":"your-user-id"},"socket_connection_url":{"websocket_url":"wss://agent-meetstream-prd-main.meetstream.ai/bridge"},"live_audio_required":{"websocket_url":"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio"},"automatic_leave":{"waiting_room_timeout":100,"everyone_left_timeout":100,"voice_inactivity_timeout":100,"in_call_recording_timeout":14400,"recording_permission_denied_timeout":60},"recording_config":{"retention":{"type":"timed","hours":24}}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go MIA (Meetstream Infrastructure Agent) package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone :wave , I'm a speaking agent\",\n \"bot_image_url\": \"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg\",\n \"agent_config_id\": \"your-agent-config-id\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n },\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n }\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby MIA (Meetstream Infrastructure Agent) require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone :wave , I'm a speaking agent\",\n \"bot_image_url\": \"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg\",\n \"agent_config_id\": \"your-agent-config-id\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n },\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n }\n }\n}" response = http.request(request) puts response.read_body ``` ```java MIA (Meetstream Infrastructure Agent) import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone :wave , I'm a speaking agent\",\n \"bot_image_url\": \"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg\",\n \"agent_config_id\": \"your-agent-config-id\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n },\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n }\n }\n}") .asString(); ``` ```php MIA (Meetstream Infrastructure Agent) request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "bot_message": "Hey Everyone :wave , I\'m a speaking agent", "bot_image_url": "https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg", "agent_config_id": "your-agent-config-id", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" }, "socket_connection_url": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge" }, "live_audio_required": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 24 } } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp MIA (Meetstream Infrastructure Agent) using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Hey Everyone :wave , I'm a speaking agent\",\n \"bot_image_url\": \"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg\",\n \"agent_config_id\": \"your-agent-config-id\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n },\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n }\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift MIA (Meetstream Infrastructure Agent) import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "bot_message": "Hey Everyone :wave , I'm a speaking agent", "bot_image_url": "https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg", "agent_config_id": "your-agent-config-id", "custom_attributes": [ "tag": "Meetstream", "sample": "testing", "user": "your-user-id" ], "socket_connection_url": ["websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge"], "live_audio_required": ["websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio"], "automatic_leave": [ "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 ], "recording_config": ["retention": [ "type": "timed", "hours": 24 ]] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Bot Lifecycle Events **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "callback_url": "https://your-server.com/webhook" } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Bot Lifecycle Events import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "callback_url": "https://your-server.com/webhook" } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Bot Lifecycle Events const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"callback_url":"https://your-server.com/webhook"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Bot Lifecycle Events package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"callback_url\": \"https://your-server.com/webhook\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Bot Lifecycle Events require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"callback_url\": \"https://your-server.com/webhook\"\n}" response = http.request(request) puts response.read_body ``` ```java Bot Lifecycle Events import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"callback_url\": \"https://your-server.com/webhook\"\n}") .asString(); ``` ```php Bot Lifecycle Events request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "callback_url": "https://your-server.com/webhook" }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Bot Lifecycle Events using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"callback_url\": \"https://your-server.com/webhook\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Bot Lifecycle Events import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "callback_url": "https://your-server.com/webhook" ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Transcription and Workflow **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "workflow_config_ids": [ "c3075e7d-d39d-4fd7-af25-c91eb9a6ac36" ], "recording_config": { "transcript": { "provider": { "deepgram": { "model": "nova-3", "language": "en", "punctuate": true, "smart_format": true, "diarize": true, "paragraphs": true, "numerals": true, "filler_words": false, "utterances": true, "utt_split": 0.8, "detect_language": false, "keywords": [ "MeetStream", "recording", "transcript" ], "search": [ "MeetStream", "recording" ], "tag": [ "custom" ] } } } } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Transcription and Workflow import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "workflow_config_ids": ["c3075e7d-d39d-4fd7-af25-c91eb9a6ac36"], "recording_config": { "transcript": { "provider": { "deepgram": { "model": "nova-3", "language": "en", "punctuate": True, "smart_format": True, "diarize": True, "paragraphs": True, "numerals": True, "filler_words": False, "utterances": True, "utt_split": 0.8, "detect_language": False, "keywords": ["MeetStream", "recording", "transcript"], "search": ["MeetStream", "recording"], "tag": ["custom"] } } } } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Transcription and Workflow const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"workflow_config_ids":["c3075e7d-d39d-4fd7-af25-c91eb9a6ac36"],"recording_config":{"transcript":{"provider":{"deepgram":{"model":"nova-3","language":"en","punctuate":true,"smart_format":true,"diarize":true,"paragraphs":true,"numerals":true,"filler_words":false,"utterances":true,"utt_split":0.8,"detect_language":false,"keywords":["MeetStream","recording","transcript"],"search":["MeetStream","recording"],"tag":["custom"]}}}}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Transcription and Workflow package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"workflow_config_ids\": [\n \"c3075e7d-d39d-4fd7-af25-c91eb9a6ac36\"\n ],\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"deepgram\": {\n \"model\": \"nova-3\",\n \"language\": \"en\",\n \"punctuate\": true,\n \"smart_format\": true,\n \"diarize\": true,\n \"paragraphs\": true,\n \"numerals\": true,\n \"filler_words\": false,\n \"utterances\": true,\n \"utt_split\": 0.8,\n \"detect_language\": false,\n \"keywords\": [\n \"MeetStream\",\n \"recording\",\n \"transcript\"\n ],\n \"search\": [\n \"MeetStream\",\n \"recording\"\n ],\n \"tag\": [\n \"custom\"\n ]\n }\n }\n }\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Transcription and Workflow require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"workflow_config_ids\": [\n \"c3075e7d-d39d-4fd7-af25-c91eb9a6ac36\"\n ],\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"deepgram\": {\n \"model\": \"nova-3\",\n \"language\": \"en\",\n \"punctuate\": true,\n \"smart_format\": true,\n \"diarize\": true,\n \"paragraphs\": true,\n \"numerals\": true,\n \"filler_words\": false,\n \"utterances\": true,\n \"utt_split\": 0.8,\n \"detect_language\": false,\n \"keywords\": [\n \"MeetStream\",\n \"recording\",\n \"transcript\"\n ],\n \"search\": [\n \"MeetStream\",\n \"recording\"\n ],\n \"tag\": [\n \"custom\"\n ]\n }\n }\n }\n }\n}" response = http.request(request) puts response.read_body ``` ```java Transcription and Workflow import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"workflow_config_ids\": [\n \"c3075e7d-d39d-4fd7-af25-c91eb9a6ac36\"\n ],\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"deepgram\": {\n \"model\": \"nova-3\",\n \"language\": \"en\",\n \"punctuate\": true,\n \"smart_format\": true,\n \"diarize\": true,\n \"paragraphs\": true,\n \"numerals\": true,\n \"filler_words\": false,\n \"utterances\": true,\n \"utt_split\": 0.8,\n \"detect_language\": false,\n \"keywords\": [\n \"MeetStream\",\n \"recording\",\n \"transcript\"\n ],\n \"search\": [\n \"MeetStream\",\n \"recording\"\n ],\n \"tag\": [\n \"custom\"\n ]\n }\n }\n }\n }\n}") .asString(); ``` ```php Transcription and Workflow request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "workflow_config_ids": [ "c3075e7d-d39d-4fd7-af25-c91eb9a6ac36" ], "recording_config": { "transcript": { "provider": { "deepgram": { "model": "nova-3", "language": "en", "punctuate": true, "smart_format": true, "diarize": true, "paragraphs": true, "numerals": true, "filler_words": false, "utterances": true, "utt_split": 0.8, "detect_language": false, "keywords": [ "MeetStream", "recording", "transcript" ], "search": [ "MeetStream", "recording" ], "tag": [ "custom" ] } } } } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Transcription and Workflow using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"workflow_config_ids\": [\n \"c3075e7d-d39d-4fd7-af25-c91eb9a6ac36\"\n ],\n \"recording_config\": {\n \"transcript\": {\n \"provider\": {\n \"deepgram\": {\n \"model\": \"nova-3\",\n \"language\": \"en\",\n \"punctuate\": true,\n \"smart_format\": true,\n \"diarize\": true,\n \"paragraphs\": true,\n \"numerals\": true,\n \"filler_words\": false,\n \"utterances\": true,\n \"utt_split\": 0.8,\n \"detect_language\": false,\n \"keywords\": [\n \"MeetStream\",\n \"recording\",\n \"transcript\"\n ],\n \"search\": [\n \"MeetStream\",\n \"recording\"\n ],\n \"tag\": [\n \"custom\"\n ]\n }\n }\n }\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Transcription and Workflow import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "workflow_config_ids": ["c3075e7d-d39d-4fd7-af25-c91eb9a6ac36"], "recording_config": ["transcript": ["provider": ["deepgram": [ "model": "nova-3", "language": "en", "punctuate": true, "smart_format": true, "diarize": true, "paragraphs": true, "numerals": true, "filler_words": false, "utterances": true, "utt_split": 0.8, "detect_language": false, "keywords": ["MeetStream", "recording", "transcript"], "search": ["MeetStream", "recording"], "tag": ["custom"] ]]]] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Video Streaming — InBound **Request** ```json { "meeting_link": "https://meet.google.com/xsr-shfj-ftv", "bot_name": "MeetStream video agent", "video_required": true, "socket_connection_url": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge" } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Video Streaming — InBound import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/xsr-shfj-ftv", "bot_name": "MeetStream video agent", "video_required": True, "socket_connection_url": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge" } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Video Streaming — InBound const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/xsr-shfj-ftv","bot_name":"MeetStream video agent","video_required":true,"socket_connection_url":{"websocket_url":"wss://agent-meetstream-prd-main.meetstream.ai/bridge"}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Video Streaming — InBound package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/xsr-shfj-ftv\",\n \"bot_name\": \"MeetStream video agent\",\n \"video_required\": true,\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Video Streaming — InBound require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/xsr-shfj-ftv\",\n \"bot_name\": \"MeetStream video agent\",\n \"video_required\": true,\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n }\n}" response = http.request(request) puts response.read_body ``` ```java Video Streaming — InBound import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/xsr-shfj-ftv\",\n \"bot_name\": \"MeetStream video agent\",\n \"video_required\": true,\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n }\n}") .asString(); ``` ```php Video Streaming — InBound request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/xsr-shfj-ftv", "bot_name": "MeetStream video agent", "video_required": true, "socket_connection_url": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge" } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Video Streaming — InBound using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/xsr-shfj-ftv\",\n \"bot_name\": \"MeetStream video agent\",\n \"video_required\": true,\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Video Streaming — InBound import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/xsr-shfj-ftv", "bot_name": "MeetStream video agent", "video_required": true, "socket_connection_url": ["websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge"] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Zoom OBF **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "zoom": { "use_zoom_obf": true }, "bot_message": "Hey Everyone :wave , I'm a speaking agent", "bot_image_url": "https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 100 } } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Zoom OBF import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "zoom": { "use_zoom_obf": True }, "bot_message": "Hey Everyone :wave , I'm a speaking agent", "bot_image_url": "https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 100 } } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Zoom OBF const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"zoom":{"use_zoom_obf":true},"bot_message":"Hey Everyone :wave , I\'m a speaking agent","bot_image_url":"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg","custom_attributes":{"tag":"Meetstream","sample":"testing","user":"your-user-id"},"automatic_leave":{"waiting_room_timeout":100,"everyone_left_timeout":100,"voice_inactivity_timeout":100,"in_call_recording_timeout":14400,"recording_permission_denied_timeout":60},"recording_config":{"retention":{"type":"timed","hours":100}}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Zoom OBF package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"zoom\": {\n \"use_zoom_obf\": true\n },\n \"bot_message\": \"Hey Everyone :wave , I'm a speaking agent\",\n \"bot_image_url\": \"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 100\n }\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Zoom OBF require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"zoom\": {\n \"use_zoom_obf\": true\n },\n \"bot_message\": \"Hey Everyone :wave , I'm a speaking agent\",\n \"bot_image_url\": \"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 100\n }\n }\n}" response = http.request(request) puts response.read_body ``` ```java Zoom OBF import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"zoom\": {\n \"use_zoom_obf\": true\n },\n \"bot_message\": \"Hey Everyone :wave , I'm a speaking agent\",\n \"bot_image_url\": \"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 100\n }\n }\n}") .asString(); ``` ```php Zoom OBF request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "zoom": { "use_zoom_obf": true }, "bot_message": "Hey Everyone :wave , I\'m a speaking agent", "bot_image_url": "https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 100 } } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Zoom OBF using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"zoom\": {\n \"use_zoom_obf\": true\n },\n \"bot_message\": \"Hey Everyone :wave , I'm a speaking agent\",\n \"bot_image_url\": \"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 100\n }\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Zoom OBF import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "zoom": ["use_zoom_obf": true], "bot_message": "Hey Everyone :wave , I'm a speaking agent", "bot_image_url": "https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg", "custom_attributes": [ "tag": "Meetstream", "sample": "testing", "user": "your-user-id" ], "automatic_leave": [ "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 ], "recording_config": ["retention": [ "type": "timed", "hours": 100 ]] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Separate Video Streams **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "video_separate_streams": true, "bot_message": "Hey Everyone :wave , I'm a speaking agent", "bot_image_url": "https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 100 } } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Separate Video Streams import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "video_separate_streams": True, "bot_message": "Hey Everyone :wave , I'm a speaking agent", "bot_image_url": "https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 100 } } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Separate Video Streams const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"video_separate_streams":true,"bot_message":"Hey Everyone :wave , I\'m a speaking agent","bot_image_url":"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg","custom_attributes":{"tag":"Meetstream","sample":"testing","user":"your-user-id"},"automatic_leave":{"waiting_room_timeout":100,"everyone_left_timeout":100,"voice_inactivity_timeout":100,"in_call_recording_timeout":14400,"recording_permission_denied_timeout":60},"recording_config":{"retention":{"type":"timed","hours":100}}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Separate Video Streams package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"video_separate_streams\": true,\n \"bot_message\": \"Hey Everyone :wave , I'm a speaking agent\",\n \"bot_image_url\": \"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 100\n }\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Separate Video Streams require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"video_separate_streams\": true,\n \"bot_message\": \"Hey Everyone :wave , I'm a speaking agent\",\n \"bot_image_url\": \"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 100\n }\n }\n}" response = http.request(request) puts response.read_body ``` ```java Separate Video Streams import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"video_separate_streams\": true,\n \"bot_message\": \"Hey Everyone :wave , I'm a speaking agent\",\n \"bot_image_url\": \"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 100\n }\n }\n}") .asString(); ``` ```php Separate Video Streams request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "video_separate_streams": true, "bot_message": "Hey Everyone :wave , I\'m a speaking agent", "bot_image_url": "https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 100 } } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Separate Video Streams using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"video_separate_streams\": true,\n \"bot_message\": \"Hey Everyone :wave , I'm a speaking agent\",\n \"bot_image_url\": \"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 100\n }\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Separate Video Streams import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "video_separate_streams": true, "bot_message": "Hey Everyone :wave , I'm a speaking agent", "bot_image_url": "https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg", "custom_attributes": [ "tag": "Meetstream", "sample": "testing", "user": "your-user-id" ], "automatic_leave": [ "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 ], "recording_config": ["retention": [ "type": "timed", "hours": 100 ]] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Separate Audio Streams **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "audio_separate_streams": true, "bot_message": "Hey Everyone :wave , I'm a speaking agent", "bot_image_url": "https://media1.tenor.com/m/DgzBbRG6ibUAAAAC/gross-eek.gif", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 51, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 48 } } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Separate Audio Streams import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "audio_separate_streams": True, "bot_message": "Hey Everyone :wave , I'm a speaking agent", "bot_image_url": "https://media1.tenor.com/m/DgzBbRG6ibUAAAAC/gross-eek.gif", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 51, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 48 } } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Separate Audio Streams const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"audio_separate_streams":true,"bot_message":"Hey Everyone :wave , I\'m a speaking agent","bot_image_url":"https://media1.tenor.com/m/DgzBbRG6ibUAAAAC/gross-eek.gif","custom_attributes":{"tag":"Meetstream","sample":"testing","user":"your-user-id"},"automatic_leave":{"waiting_room_timeout":100,"everyone_left_timeout":100,"voice_inactivity_timeout":51,"in_call_recording_timeout":14400,"recording_permission_denied_timeout":60},"recording_config":{"retention":{"type":"timed","hours":48}}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Separate Audio Streams package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"audio_separate_streams\": true,\n \"bot_message\": \"Hey Everyone :wave , I'm a speaking agent\",\n \"bot_image_url\": \"https://media1.tenor.com/m/DgzBbRG6ibUAAAAC/gross-eek.gif\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 51,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 48\n }\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Separate Audio Streams require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"audio_separate_streams\": true,\n \"bot_message\": \"Hey Everyone :wave , I'm a speaking agent\",\n \"bot_image_url\": \"https://media1.tenor.com/m/DgzBbRG6ibUAAAAC/gross-eek.gif\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 51,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 48\n }\n }\n}" response = http.request(request) puts response.read_body ``` ```java Separate Audio Streams import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"audio_separate_streams\": true,\n \"bot_message\": \"Hey Everyone :wave , I'm a speaking agent\",\n \"bot_image_url\": \"https://media1.tenor.com/m/DgzBbRG6ibUAAAAC/gross-eek.gif\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 51,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 48\n }\n }\n}") .asString(); ``` ```php Separate Audio Streams request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "audio_separate_streams": true, "bot_message": "Hey Everyone :wave , I\'m a speaking agent", "bot_image_url": "https://media1.tenor.com/m/DgzBbRG6ibUAAAAC/gross-eek.gif", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 51, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 48 } } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Separate Audio Streams using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"audio_separate_streams\": true,\n \"bot_message\": \"Hey Everyone :wave , I'm a speaking agent\",\n \"bot_image_url\": \"https://media1.tenor.com/m/DgzBbRG6ibUAAAAC/gross-eek.gif\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 51,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 48\n }\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Separate Audio Streams import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "audio_separate_streams": true, "bot_message": "Hey Everyone :wave , I'm a speaking agent", "bot_image_url": "https://media1.tenor.com/m/DgzBbRG6ibUAAAAC/gross-eek.gif", "custom_attributes": [ "tag": "Meetstream", "sample": "testing", "user": "your-user-id" ], "automatic_leave": [ "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 51, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 ], "recording_config": ["retention": [ "type": "timed", "hours": 48 ]] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### MIA Virtual Avatars **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "bot_message": "Avatar bot here", "bot_image_url": "https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg", "agent_config_id": "your-agent-config-id", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" }, "socket_connection_url": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge" }, "live_audio_required": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 24 } } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python MIA Virtual Avatars import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "bot_message": "Avatar bot here", "bot_image_url": "https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg", "agent_config_id": "your-agent-config-id", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" }, "socket_connection_url": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge" }, "live_audio_required": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 24 } } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript MIA Virtual Avatars const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"bot_message":"Avatar bot here","bot_image_url":"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg","agent_config_id":"your-agent-config-id","custom_attributes":{"tag":"Meetstream","sample":"testing","user":"your-user-id"},"socket_connection_url":{"websocket_url":"wss://agent-meetstream-prd-main.meetstream.ai/bridge"},"live_audio_required":{"websocket_url":"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio"},"automatic_leave":{"waiting_room_timeout":100,"everyone_left_timeout":100,"voice_inactivity_timeout":100,"in_call_recording_timeout":14400,"recording_permission_denied_timeout":60},"recording_config":{"retention":{"type":"timed","hours":24}}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go MIA Virtual Avatars package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Avatar bot here\",\n \"bot_image_url\": \"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg\",\n \"agent_config_id\": \"your-agent-config-id\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n },\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n }\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby MIA Virtual Avatars require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Avatar bot here\",\n \"bot_image_url\": \"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg\",\n \"agent_config_id\": \"your-agent-config-id\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n },\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n }\n }\n}" response = http.request(request) puts response.read_body ``` ```java MIA Virtual Avatars import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Avatar bot here\",\n \"bot_image_url\": \"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg\",\n \"agent_config_id\": \"your-agent-config-id\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n },\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n }\n }\n}") .asString(); ``` ```php MIA Virtual Avatars request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "bot_message": "Avatar bot here", "bot_image_url": "https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg", "agent_config_id": "your-agent-config-id", "custom_attributes": { "tag": "Meetstream", "sample": "testing", "user": "your-user-id" }, "socket_connection_url": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge" }, "live_audio_required": { "websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio" }, "automatic_leave": { "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 }, "recording_config": { "retention": { "type": "timed", "hours": 24 } } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp MIA Virtual Avatars using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"bot_message\": \"Avatar bot here\",\n \"bot_image_url\": \"https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg\",\n \"agent_config_id\": \"your-agent-config-id\",\n \"custom_attributes\": {\n \"tag\": \"Meetstream\",\n \"sample\": \"testing\",\n \"user\": \"your-user-id\"\n },\n \"socket_connection_url\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge\"\n },\n \"live_audio_required\": {\n \"websocket_url\": \"wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio\"\n },\n \"automatic_leave\": {\n \"waiting_room_timeout\": 100,\n \"everyone_left_timeout\": 100,\n \"voice_inactivity_timeout\": 100,\n \"in_call_recording_timeout\": 14400,\n \"recording_permission_denied_timeout\": 60\n },\n \"recording_config\": {\n \"retention\": {\n \"type\": \"timed\",\n \"hours\": 24\n }\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift MIA Virtual Avatars import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "bot_message": "Avatar bot here", "bot_image_url": "https://www.malwarebytes.com/wp-content/uploads/sites/2/2024/08/Grok_logo.jpg", "agent_config_id": "your-agent-config-id", "custom_attributes": [ "tag": "Meetstream", "sample": "testing", "user": "your-user-id" ], "socket_connection_url": ["websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge"], "live_audio_required": ["websocket_url": "wss://agent-meetstream-prd-main.meetstream.ai/bridge/audio"], "automatic_leave": [ "waiting_room_timeout": 100, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 ], "recording_config": ["retention": [ "type": "timed", "hours": 24 ]] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Chat Notifications on Webhook **Request** ```json { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "recording_config": { "realtime_endpoints": [ { "type": "webhook", "url": "https://your-server.com/webhook", "events": [ "participant_events.chat_message" ] } ] } } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": null, "meeting_url": "https://meet.google.com/xsr-shfj-ftv", "status": "Active" } ``` **SDK Code** ```python Chat Notifications on Webhook import requests url = "https://api.meetstream.ai/api/v1/bots/create_bot" payload = { "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": True, "recording_config": { "realtime_endpoints": [ { "type": "webhook", "url": "https://your-server.com/webhook", "events": ["participant_events.chat_message"] } ] } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Chat Notifications on Webhook const url = 'https://api.meetstream.ai/api/v1/bots/create_bot'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"meeting_link":"https://meet.google.com/zcs-xwyv-hvi","bot_name":"Meetstream Agent","video_required":true,"recording_config":{"realtime_endpoints":[{"type":"webhook","url":"https://your-server.com/webhook","events":["participant_events.chat_message"]}]}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Chat Notifications on Webhook package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/create_bot" payload := strings.NewReader("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"recording_config\": {\n \"realtime_endpoints\": [\n {\n \"type\": \"webhook\",\n \"url\": \"https://your-server.com/webhook\",\n \"events\": [\n \"participant_events.chat_message\"\n ]\n }\n ]\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Chat Notifications on Webhook require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/create_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"recording_config\": {\n \"realtime_endpoints\": [\n {\n \"type\": \"webhook\",\n \"url\": \"https://your-server.com/webhook\",\n \"events\": [\n \"participant_events.chat_message\"\n ]\n }\n ]\n }\n}" response = http.request(request) puts response.read_body ``` ```java Chat Notifications on Webhook import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/create_bot") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"recording_config\": {\n \"realtime_endpoints\": [\n {\n \"type\": \"webhook\",\n \"url\": \"https://your-server.com/webhook\",\n \"events\": [\n \"participant_events.chat_message\"\n ]\n }\n ]\n }\n}") .asString(); ``` ```php Chat Notifications on Webhook request('POST', 'https://api.meetstream.ai/api/v1/bots/create_bot', [ 'body' => '{ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "recording_config": { "realtime_endpoints": [ { "type": "webhook", "url": "https://your-server.com/webhook", "events": [ "participant_events.chat_message" ] } ] } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Chat Notifications on Webhook using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/create_bot"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"meeting_link\": \"https://meet.google.com/zcs-xwyv-hvi\",\n \"bot_name\": \"Meetstream Agent\",\n \"video_required\": true,\n \"recording_config\": {\n \"realtime_endpoints\": [\n {\n \"type\": \"webhook\",\n \"url\": \"https://your-server.com/webhook\",\n \"events\": [\n \"participant_events.chat_message\"\n ]\n }\n ]\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Chat Notifications on Webhook import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "meeting_link": "https://meet.google.com/zcs-xwyv-hvi", "bot_name": "Meetstream Agent", "video_required": true, "recording_config": ["realtime_endpoints": [ [ "type": "webhook", "url": "https://your-server.com/webhook", "events": ["participant_events.chat_message"] ] ]] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/create_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Reschedule Bot PATCH https://api.meetstream.ai/api/v1/calendar/scheduled_bots/{bot_id} Content-Type: application/json Reference: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/reschedule-bot ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/calendar/scheduled_bots/{bot_id}: patch: operationId: reschedule-bot summary: Reschedule Bot tags: - subpackage_bots parameters: - name: bot_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/RescheduleBotResponse' requestBody: content: application/json: schema: $ref: '#/components/schemas/RescheduleBotRequest' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: RescheduleBotRequest: type: object properties: scheduled_join_time: type: string format: date-time required: - scheduled_join_time title: RescheduleBotRequest RescheduleBotResponse: type: object properties: message: type: string bot_id: type: string updated_fields: type: array items: type: string schedule_updated: type: boolean title: RescheduleBotResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Request** ```json { "scheduled_join_time": "2025-11-24T14:30:00Z" } ``` **Response** ```json { "message": "Scheduled bot updated successfully", "bot_id": "e4ba67d3-474b-4dee-8e3e-c9a6fa088af6", "updated_fields": [ "scheduled_join_time" ], "schedule_updated": true } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/calendar/scheduled_bots/bot_id" payload = { "scheduled_join_time": "2025-11-24T14:30:00Z" } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.patch(url, json=payload, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/calendar/scheduled_bots/bot_id'; const options = { method: 'PATCH', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"scheduled_join_time":"2025-11-24T14:30:00Z"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/calendar/scheduled_bots/bot_id" payload := strings.NewReader("{\n \"scheduled_join_time\": \"2025-11-24T14:30:00Z\"\n}") req, _ := http.NewRequest("PATCH", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/calendar/scheduled_bots/bot_id") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Patch.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"scheduled_join_time\": \"2025-11-24T14:30:00Z\"\n}" response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.patch("https://api.meetstream.ai/api/v1/calendar/scheduled_bots/bot_id") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"scheduled_join_time\": \"2025-11-24T14:30:00Z\"\n}") .asString(); ``` ```php request('PATCH', 'https://api.meetstream.ai/api/v1/calendar/scheduled_bots/bot_id', [ 'body' => '{ "scheduled_join_time": "2025-11-24T14:30:00Z" }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/calendar/scheduled_bots/bot_id"); var request = new RestRequest(Method.PATCH); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"scheduled_join_time\": \"2025-11-24T14:30:00Z\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = ["scheduled_join_time": "2025-11-24T14:30:00Z"] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/calendar/scheduled_bots/bot_id")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "PATCH" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Delete Scheduled Bot DELETE https://api.meetstream.ai/api/v1/calendar/scheduled_bots/{bot_id} Reference: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/delete-scheduled-bot ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/calendar/scheduled_bots/{bot_id}: delete: operationId: delete-scheduled-bot summary: Delete Scheduled Bot tags: - subpackage_bots parameters: - name: bot_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/DeleteScheduledBotResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: DeleteScheduledBotResponse: type: object properties: message: type: string bot_id: type: string title: DeleteScheduledBotResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "message": "Scheduled bot deleted successfully", "bot_id": "e4ba67d3-474b-4dee-8e3e-c9a6fa088af6" } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/calendar/scheduled_bots/bot_id" headers = {"Authorization": ""} response = requests.delete(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/calendar/scheduled_bots/bot_id'; const options = {method: 'DELETE', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/calendar/scheduled_bots/bot_id" req, _ := http.NewRequest("DELETE", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/calendar/scheduled_bots/bot_id") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Delete.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.delete("https://api.meetstream.ai/api/v1/calendar/scheduled_bots/bot_id") .header("Authorization", "") .asString(); ``` ```php request('DELETE', 'https://api.meetstream.ai/api/v1/calendar/scheduled_bots/bot_id', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/calendar/scheduled_bots/bot_id"); var request = new RestRequest(Method.DELETE); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/calendar/scheduled_bots/bot_id")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "DELETE" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Get Bot Status GET https://api.meetstream.ai/api/v1/bots/{bot_id}/status Reference: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/get-bot-status ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/bots/{bot_id}/status: get: operationId: get-bot-status summary: Get Bot Status tags: - subpackage_bots parameters: - name: bot_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/BotStatusResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: BotStatusResponse: type: object properties: bot_id: type: string status: type: string custom_attributes: type: object additionalProperties: description: Any type title: BotStatusResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "bot_id": "6e1516f8-5518-413b-9d79-93000f8cb3b9", "status": "NotAllowed", "custom_attributes": {} } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/bots/bot_id/status" headers = {"Authorization": ""} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/bots/bot_id/status'; const options = {method: 'GET', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/bot_id/status" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/bot_id/status") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.meetstream.ai/api/v1/bots/bot_id/status") .header("Authorization", "") .asString(); ``` ```php request('GET', 'https://api.meetstream.ai/api/v1/bots/bot_id/status', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/bot_id/status"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/bot_id/status")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Get Bot Details GET https://api.meetstream.ai/api/v1/bots/{bot_id}/detail Participants activity can be fetched from the link in the response payload. Reference: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/get-bot-details ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/bots/{bot_id}/detail: get: operationId: get-bot-details summary: Get Bot Details description: >- Participants activity can be fetched from the link in the response payload. tags: - subpackage_bots parameters: - name: bot_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/BotDetailsResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: BotDetailsResponseBotDetailsCaptionFile: type: object properties: available: type: boolean title: BotDetailsResponseBotDetailsCaptionFile BotDetailsResponseBotDetails: type: object properties: BotID: type: string BotImageURL: type: - string - 'null' BotMessage: type: string BotProfile: type: string BotUsername: type: string CreatedAt: type: string Duration: type: integer EndTime: type: string LastUpdatedAt: type: integer ManifestStatus: type: string MeetingLink: type: string NativeSTT: type: boolean OfferingType: type: string Platform: type: string StartTime: type: string Status: type: string UserID: type: string RequestPayload: type: object additionalProperties: description: Any type StatusTimeline: type: object additionalProperties: description: Any type custom_attributes: type: object additionalProperties: description: Any type caption_file: $ref: '#/components/schemas/BotDetailsResponseBotDetailsCaptionFile' title: BotDetailsResponseBotDetails BotDetailsResponse: type: object properties: bot_details: $ref: '#/components/schemas/BotDetailsResponseBotDetails' title: BotDetailsResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "bot_details": { "BotID": "e8b71172-ed31-4915-a51a-3a9e50d699a7", "BotImageURL": "string", "BotMessage": "Bot joining meeting from calendar integration", "BotProfile": "default", "BotUsername": "Calendar Bot", "CreatedAt": "2026-03-19T13:57:18Z", "Duration": 615, "EndTime": "2026-03-19T14:08:08Z", "LastUpdatedAt": 1773929288, "ManifestStatus": "Skipped", "MeetingLink": "https://meet.google.com/wxj-cnvx-wdw", "NativeSTT": false, "OfferingType": "base_price", "Platform": "GMeet", "StartTime": "2026-03-19T13:57:52.810Z", "Status": "NotAllowed", "UserID": "945804c8-90d1-7078-4e28-959174e4f499", "RequestPayload": {}, "StatusTimeline": {}, "custom_attributes": {}, "caption_file": { "available": false } } } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/bots/bot_id/detail" headers = {"Authorization": ""} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/bots/bot_id/detail'; const options = {method: 'GET', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/bot_id/detail" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/bot_id/detail") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.meetstream.ai/api/v1/bots/bot_id/detail") .header("Authorization", "") .asString(); ``` ```php request('GET', 'https://api.meetstream.ai/api/v1/bots/bot_id/detail', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/bot_id/detail"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/bot_id/detail")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Get Bot Summary GET https://api.meetstream.ai/api/v1/bots/{bot_id}/summary Reference: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/get-bot-summary ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/bots/{bot_id}/summary: get: operationId: get-bot-summary summary: Get Bot Summary tags: - subpackage_bots parameters: - name: bot_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/BotDetailsResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: BotDetailsResponseBotDetailsCaptionFile: type: object properties: available: type: boolean title: BotDetailsResponseBotDetailsCaptionFile BotDetailsResponseBotDetails: type: object properties: BotID: type: string BotImageURL: type: - string - 'null' BotMessage: type: string BotProfile: type: string BotUsername: type: string CreatedAt: type: string Duration: type: integer EndTime: type: string LastUpdatedAt: type: integer ManifestStatus: type: string MeetingLink: type: string NativeSTT: type: boolean OfferingType: type: string Platform: type: string StartTime: type: string Status: type: string UserID: type: string RequestPayload: type: object additionalProperties: description: Any type StatusTimeline: type: object additionalProperties: description: Any type custom_attributes: type: object additionalProperties: description: Any type caption_file: $ref: '#/components/schemas/BotDetailsResponseBotDetailsCaptionFile' title: BotDetailsResponseBotDetails BotDetailsResponse: type: object properties: bot_details: $ref: '#/components/schemas/BotDetailsResponseBotDetails' title: BotDetailsResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "bot_details": { "BotID": "e8b71172-ed31-4915-a51a-3a9e50d699a7", "BotImageURL": "string", "BotMessage": "Bot joining meeting from calendar integration", "BotProfile": "default", "BotUsername": "Calendar Bot", "CreatedAt": "2026-03-19T13:57:18Z", "Duration": 615, "EndTime": "2026-03-19T14:08:08Z", "LastUpdatedAt": 1773929288, "ManifestStatus": "Skipped", "MeetingLink": "https://meet.google.com/wxj-cnvx-wdw", "NativeSTT": false, "OfferingType": "base_price", "Platform": "GMeet", "StartTime": "2026-03-19T13:57:52.810Z", "Status": "NotAllowed", "UserID": "945804c8-90d1-7078-4e28-959174e4f499", "RequestPayload": {}, "StatusTimeline": {}, "custom_attributes": {}, "caption_file": { "available": false } } } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/bots/bot_id/summary" headers = {"Authorization": ""} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/bots/bot_id/summary'; const options = {method: 'GET', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/bot_id/summary" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/bot_id/summary") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.meetstream.ai/api/v1/bots/bot_id/summary") .header("Authorization", "") .asString(); ``` ```php request('GET', 'https://api.meetstream.ai/api/v1/bots/bot_id/summary', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/bot_id/summary"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/bot_id/summary")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Get Bot Audio GET https://api.meetstream.ai/api/v1/bots/{bot_id}/get_audio Reference: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/get-bot-audio ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/bots/{bot_id}/get_audio: get: operationId: get-bot-audio summary: Get Bot Audio tags: - subpackage_bots parameters: - name: bot_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/BotAudioResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: BotAudioResponse: type: object properties: audio_url: type: string description: >- A pre-signed S3 URL to download the bot's recorded audio. Valid for 1 hour. title: BotAudioResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "audio_url": "https://ms-s3-bucket.s3.amazonaws.com/audio.wav" } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/bots/bot_id/get_audio" headers = {"Authorization": ""} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/bots/bot_id/get_audio'; const options = {method: 'GET', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/bot_id/get_audio" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/bot_id/get_audio") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.meetstream.ai/api/v1/bots/bot_id/get_audio") .header("Authorization", "") .asString(); ``` ```php request('GET', 'https://api.meetstream.ai/api/v1/bots/bot_id/get_audio', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/bot_id/get_audio"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/bot_id/get_audio")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Get Bot Video GET https://api.meetstream.ai/api/v1/bots/{bot_id}/get_video Reference: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/get-bot-video ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/bots/{bot_id}/get_video: get: operationId: get-bot-video summary: Get Bot Video tags: - subpackage_bots parameters: - name: bot_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/BotVideoResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: BotVideoResponseVideoInfo: type: object properties: duration: type: - integer - 'null' size_mb: type: - number - 'null' format: double resolution: type: string fps: type: - integer - 'null' frames_processed: type: - integer - 'null' corrupted_frames: type: integer title: BotVideoResponseVideoInfo BotVideoResponse: type: object properties: video_url: type: string description: >- A pre-signed S3 URL to download the bot's recorded video. Valid for 10 minutes. video_info: $ref: '#/components/schemas/BotVideoResponseVideoInfo' title: BotVideoResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "video_url": "https://ms-s3-bucket.s3.amazonaws.com/meeting_recording.mp4", "video_info": { "duration": 1, "size_mb": 1.1, "resolution": "1920x1080", "fps": 1, "frames_processed": 1, "corrupted_frames": 0 } } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/bots/bot_id/get_video" headers = {"Authorization": ""} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/bots/bot_id/get_video'; const options = {method: 'GET', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/bot_id/get_video" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/bot_id/get_video") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.meetstream.ai/api/v1/bots/bot_id/get_video") .header("Authorization", "") .asString(); ``` ```php request('GET', 'https://api.meetstream.ai/api/v1/bots/bot_id/get_video', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/bot_id/get_video"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/bot_id/get_video")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Get Recording Streams GET https://api.meetstream.ai/api/v1/bots/{bot_id}/get_recording_streams Reference: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/get-recording-streams ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/bots/{bot_id}/get_recording_streams: get: operationId: get-recording-streams summary: Get Recording Streams tags: - subpackage_bots parameters: - name: bot_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/BotVideoResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: BotVideoResponseVideoInfo: type: object properties: duration: type: - integer - 'null' size_mb: type: - number - 'null' format: double resolution: type: string fps: type: - integer - 'null' frames_processed: type: - integer - 'null' corrupted_frames: type: integer title: BotVideoResponseVideoInfo BotVideoResponse: type: object properties: video_url: type: string description: >- A pre-signed S3 URL to download the bot's recorded video. Valid for 10 minutes. video_info: $ref: '#/components/schemas/BotVideoResponseVideoInfo' title: BotVideoResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "video_url": "https://ms-s3-bucket.s3.amazonaws.com/meeting_recording.mp4", "video_info": { "duration": 1, "size_mb": 1.1, "resolution": "1920x1080", "fps": 1, "frames_processed": 1, "corrupted_frames": 0 } } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/bots/bot_id/get_recording_streams" headers = {"Authorization": ""} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/bots/bot_id/get_recording_streams'; const options = {method: 'GET', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/bot_id/get_recording_streams" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/bot_id/get_recording_streams") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.meetstream.ai/api/v1/bots/bot_id/get_recording_streams") .header("Authorization", "") .asString(); ``` ```php request('GET', 'https://api.meetstream.ai/api/v1/bots/bot_id/get_recording_streams', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/bot_id/get_recording_streams"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/bot_id/get_recording_streams")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Get Audio Streams GET https://api.meetstream.ai/api/v1/bots/{bot_id}/get_audio_streams Reference: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/get-audio-streams ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/bots/{bot_id}/get_audio_streams: get: operationId: get-audio-streams summary: Get Audio Streams tags: - subpackage_bots parameters: - name: bot_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/BotVideoResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: BotVideoResponseVideoInfo: type: object properties: duration: type: - integer - 'null' size_mb: type: - number - 'null' format: double resolution: type: string fps: type: - integer - 'null' frames_processed: type: - integer - 'null' corrupted_frames: type: integer title: BotVideoResponseVideoInfo BotVideoResponse: type: object properties: video_url: type: string description: >- A pre-signed S3 URL to download the bot's recorded video. Valid for 10 minutes. video_info: $ref: '#/components/schemas/BotVideoResponseVideoInfo' title: BotVideoResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "video_url": "https://ms-s3-bucket.s3.amazonaws.com/meeting_recording.mp4", "video_info": { "duration": 1, "size_mb": 1.1, "resolution": "1920x1080", "fps": 1, "frames_processed": 1, "corrupted_frames": 0 } } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/bots/bot_id/get_audio_streams" headers = {"Authorization": ""} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/bots/bot_id/get_audio_streams'; const options = {method: 'GET', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/bot_id/get_audio_streams" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/bot_id/get_audio_streams") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.meetstream.ai/api/v1/bots/bot_id/get_audio_streams") .header("Authorization", "") .asString(); ``` ```php request('GET', 'https://api.meetstream.ai/api/v1/bots/bot_id/get_audio_streams', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/bot_id/get_audio_streams"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/bot_id/get_audio_streams")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Remove Bot GET https://api.meetstream.ai/api/v1/bots/{bot_id}/remove_bot Reference: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/remove-bot ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/bots/{bot_id}/remove_bot: get: operationId: remove-bot summary: Remove Bot tags: - subpackage_bots parameters: - name: bot_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/RemoveBotResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: RemoveBotResponse: type: object properties: message: type: string title: RemoveBotResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "message": "Stop signal sent for bot 4a25ad1f-2266-4cd3-aff0-76f9da05d1d1." } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/bots/bot_id/remove_bot" headers = {"Authorization": ""} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/bots/bot_id/remove_bot'; const options = {method: 'GET', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/bot_id/remove_bot" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/bot_id/remove_bot") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.meetstream.ai/api/v1/bots/bot_id/remove_bot") .header("Authorization", "") .asString(); ``` ```php request('GET', 'https://api.meetstream.ai/api/v1/bots/bot_id/remove_bot', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/bot_id/remove_bot"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/bot_id/remove_bot")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Get Speaker Timeline GET https://api.meetstream.ai/api/v1/bots/{bot_id}/get_speaker_timeline Reference: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/get-speaker-timeline ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/bots/{bot_id}/get_speaker_timeline: get: operationId: get-speaker-timeline summary: Get Speaker Timeline tags: - subpackage_bots parameters: - name: bot_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/SpeakerTimelineResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: SpeakerTimelineResponseChunksItems: type: object properties: chunkIndex: type: integer timestamp: type: string sampleRate: type: integer speakerId: type: string speakerName: type: string startByte: type: integer endByte: type: integer title: SpeakerTimelineResponseChunksItems SpeakerTimelineResponse: type: object properties: chunks: type: array items: $ref: '#/components/schemas/SpeakerTimelineResponseChunksItems' lastUpdated: type: string audioFilePath: type: string totalFileSize: type: integer title: SpeakerTimelineResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "chunks": [ { "chunkIndex": 0, "timestamp": "115360815932", "sampleRate": 48000, "speakerId": "spaces/iWI9CBO8PnYB/devices/242", "speakerName": "Maddy Singh", "startByte": 0, "endByte": 98300 } ], "lastUpdated": "2026-03-20T04:54:51.493Z", "audioFilePath": "/mnt/ms-efs/1f4ea39e-3197-4188-ada0-cb0fe04cc789/audio/chunks/audio.pcm", "totalFileSize": 7855808 } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/bots/bot_id/get_speaker_timeline" headers = {"Authorization": ""} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/bots/bot_id/get_speaker_timeline'; const options = {method: 'GET', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/bot_id/get_speaker_timeline" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/bot_id/get_speaker_timeline") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.meetstream.ai/api/v1/bots/bot_id/get_speaker_timeline") .header("Authorization", "") .asString(); ``` ```php request('GET', 'https://api.meetstream.ai/api/v1/bots/bot_id/get_speaker_timeline', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/bot_id/get_speaker_timeline"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/bot_id/get_speaker_timeline")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Get Bot Chats GET https://api.meetstream.ai/api/v1/bots/{bot_id}/get_chats Reference: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/get-bot-chats ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/bots/{bot_id}/get_chats: get: operationId: get-bot-chats summary: Get Bot Chats tags: - subpackage_bots parameters: - name: bot_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/BotChatsResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: BotChatsResponseMetadata: type: object properties: sessionId: type: integer startTime: type: string lastUpdated: type: string totalMessages: type: integer uniqueSpeakers: type: integer botId: type: string meetingId: type: string title: BotChatsResponseMetadata BotChatsResponseSpeakersItems: type: object properties: deviceId: type: string name: type: string displayName: type: string messageCount: type: integer title: BotChatsResponseSpeakersItems BotChatsResponseChatMessagesItems: type: object properties: messageId: type: string deviceId: type: string timestamp: type: string text: type: string speakerName: type: string speakerDisplayName: type: string clientTimestamp: type: string sessionTimestamp: type: integer title: BotChatsResponseChatMessagesItems BotChatsResponseServerMetadata: type: object properties: savedAt: type: string serverBotId: type: string serverMeetingId: type: string serverVersion: type: string title: BotChatsResponseServerMetadata BotChatsResponse: type: object properties: metadata: $ref: '#/components/schemas/BotChatsResponseMetadata' speakers: type: array items: $ref: '#/components/schemas/BotChatsResponseSpeakersItems' chatMessages: type: array items: $ref: '#/components/schemas/BotChatsResponseChatMessagesItems' messagesBySpeaker: type: object additionalProperties: description: Any type serverMetadata: $ref: '#/components/schemas/BotChatsResponseServerMetadata' title: BotChatsResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "metadata": { "sessionId": 1774024476936, "startTime": "2026-03-20T16:34:36.936Z", "lastUpdated": "2026-03-20T17:22:49.989Z", "totalMessages": 21, "uniqueSpeakers": 6, "botId": "a6719b2c-40a6-46d1-901b-9f59f28c30e3", "meetingId": "https://meet.google.com/zov-guod-aqb" }, "speakers": [ { "deviceId": "spaces/j1Gp5_7D4hUB/devices/199", "name": "Ansh Mishra", "displayName": "Ansh", "messageCount": 2 } ], "chatMessages": [ { "messageId": "spaces/j1Gp5_7D4hUB/messages/6dPwIBRKzyA.6dPwIBRKzyA", "deviceId": "spaces/j1Gp5_7D4hUB/devices/199", "timestamp": "2026-03-20T16:37:29.372Z", "text": "5 languages", "speakerName": "Ansh Mishra", "speakerDisplayName": "Ansh", "clientTimestamp": "2026-03-20T16:37:29.372Z", "sessionTimestamp": 172287 } ], "messagesBySpeaker": {}, "serverMetadata": { "savedAt": "2026-03-20T17:22:49.989Z", "serverBotId": "a6719b2c-40a6-46d1-901b-9f59f28c30e3", "serverMeetingId": "https://meet.google.com/zov-guod-aqb", "serverVersion": "1.0.0" } } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/bots/bot_id/get_chats" headers = {"Authorization": ""} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/bots/bot_id/get_chats'; const options = {method: 'GET', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/bot_id/get_chats" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/bot_id/get_chats") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.meetstream.ai/api/v1/bots/bot_id/get_chats") .header("Authorization", "") .asString(); ``` ```php request('GET', 'https://api.meetstream.ai/api/v1/bots/bot_id/get_chats', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/bot_id/get_chats"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/bot_id/get_chats")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Get Bot Screenshots GET https://api.meetstream.ai/api/v1/bots/{bot_id}/get_screenshots Reference: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/get-bot-screenshots ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/bots/{bot_id}/get_screenshots: get: operationId: get-bot-screenshots summary: Get Bot Screenshots tags: - subpackage_bots parameters: - name: bot_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/BotScreenshotsResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: BotScreenshotsResponseScreenshotsItems: type: object properties: {} title: BotScreenshotsResponseScreenshotsItems BotScreenshotsResponse: type: object properties: bot_id: type: string screenshots_count: type: integer screenshots: type: array items: $ref: '#/components/schemas/BotScreenshotsResponseScreenshotsItems' title: BotScreenshotsResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "bot_id": "6e1516f8-5518-413b-9d79-93000f8cb3b9", "screenshots_count": 0, "screenshots": [ {} ] } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/bots/bot_id/get_screenshots" headers = {"Authorization": ""} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/bots/bot_id/get_screenshots'; const options = {method: 'GET', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/bot_id/get_screenshots" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/bot_id/get_screenshots") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.meetstream.ai/api/v1/bots/bot_id/get_screenshots") .header("Authorization", "") .asString(); ``` ```php request('GET', 'https://api.meetstream.ai/api/v1/bots/bot_id/get_screenshots', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/bot_id/get_screenshots"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/bot_id/get_screenshots")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Fetch Participants GET https://api.meetstream.ai/api/v1/bots/{bot_id}/get_participants Reference: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/fetch-participants ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/bots/{bot_id}/get_participants: get: operationId: fetch-participants summary: Fetch Participants tags: - subpackage_bots parameters: - name: bot_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/FetchParticipantsResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: FetchParticipantsResponseItems: type: object properties: deviceId: type: string displayName: type: string fullName: type: string profilePicture: type: string status: type: integer humanized_status: type: string streamIds: type: array items: type: string lastUpdated: type: string parentDeviceId: type: string description: Present only for secondary devices. title: FetchParticipantsResponseItems FetchParticipantsResponse: type: array items: $ref: '#/components/schemas/FetchParticipantsResponseItems' title: FetchParticipantsResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json [ { "deviceId": "spaces/j1Gp5_7D4hUB/devices/185", "displayName": "Sidhdharth", "fullName": "Sidhdharth Sivasubramanian", "profilePicture": "https://lh3.googleusercontent.com/a/profile-picture-url", "status": 6, "humanized_status": "not_in_meeting", "streamIds": [ "2287132722", "3329978528" ], "lastUpdated": "2026-03-20T17:20:58.375Z", "parentDeviceId": "spaces/j1Gp5_7D4hUB/devices/185" } ] ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/bots/bot_id/get_participants" headers = {"Authorization": ""} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/bots/bot_id/get_participants'; const options = {method: 'GET', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/bot_id/get_participants" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/bot_id/get_participants") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.meetstream.ai/api/v1/bots/bot_id/get_participants") .header("Authorization", "") .asString(); ``` ```php request('GET', 'https://api.meetstream.ai/api/v1/bots/bot_id/get_participants', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/bot_id/get_participants"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/bot_id/get_participants")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Delete Data DELETE https://api.meetstream.ai/api/v1/bots/{bot_id}/delete Reference: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/delete-bot-data ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/bots/{bot_id}/delete: delete: operationId: delete-bot-data summary: Delete Data tags: - subpackage_bots parameters: - name: bot_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/DeleteDataResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: DeleteDataResponse: type: object properties: message: type: string bot_id: type: string deleted_objects: type: integer title: DeleteDataResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "message": "No data found for bot 6e1516f8-5518-413b-9d79-93000f8cb3b9 in S3", "bot_id": "6e1516f8-5518-413b-9d79-93000f8cb3b9", "deleted_objects": 0 } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/bots/bot_id/delete" headers = {"Authorization": ""} response = requests.delete(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/bots/bot_id/delete'; const options = {method: 'DELETE', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/bot_id/delete" req, _ := http.NewRequest("DELETE", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/bot_id/delete") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Delete.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.delete("https://api.meetstream.ai/api/v1/bots/bot_id/delete") .header("Authorization", "") .asString(); ``` ```php request('DELETE', 'https://api.meetstream.ai/api/v1/bots/bot_id/delete', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/bot_id/delete"); var request = new RestRequest(Method.DELETE); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/bot_id/delete")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "DELETE" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # List Bots GET https://api.meetstream.ai/api/v1/bots Reference: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/list-bots ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/bots: get: operationId: list-bots summary: List Bots tags: - subpackage_bots parameters: - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/ListBotsResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: ListBotsResponse: type: object properties: bots: type: array items: type: object additionalProperties: description: Any type hasNextPage: type: boolean nextCursor: type: - string - 'null' title: ListBotsResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "bots": [ {} ], "hasNextPage": false, "nextCursor": "string" } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/bots" headers = {"Authorization": ""} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/bots'; const options = {method: 'GET', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.meetstream.ai/api/v1/bots") .header("Authorization", "") .asString(); ``` ```php request('GET', 'https://api.meetstream.ai/api/v1/bots', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Send Message POST https://api.meetstream.ai/api/v1/bots/{bot_id}/send_message Content-Type: application/json Reference: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/send-message ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/bots/{bot_id}/send_message: post: operationId: send-message summary: Send Message tags: - subpackage_bots parameters: - name: bot_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/SendMessageResponse' requestBody: content: application/json: schema: $ref: '#/components/schemas/SendMessageRequest' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: SendMessageRequestMetadata: type: object properties: message_type: type: string title: SendMessageRequestMetadata SendMessageRequest: type: object properties: message: type: string metadata: $ref: '#/components/schemas/SendMessageRequestMetadata' required: - message title: SendMessageRequest SendMessageResponse: type: object properties: status: type: string bot_id: type: string command: type: string title: SendMessageResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Request** ```json { "message": "hello from meetstream" } ``` **Response** ```json { "status": "accepted", "bot_id": "526b6061-220f-47a9-972c-134b528c91e2", "command": "sendmsg" } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/bots/bot_id/send_message" payload = { "message": "hello from meetstream" } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/bots/bot_id/send_message'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"message":"hello from meetstream"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/bot_id/send_message" payload := strings.NewReader("{\n \"message\": \"hello from meetstream\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/bot_id/send_message") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"message\": \"hello from meetstream\"\n}" response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/bot_id/send_message") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"message\": \"hello from meetstream\"\n}") .asString(); ``` ```php request('POST', 'https://api.meetstream.ai/api/v1/bots/bot_id/send_message', [ 'body' => '{ "message": "hello from meetstream" }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/bot_id/send_message"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"message\": \"hello from meetstream\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = ["message": "hello from meetstream"] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/bot_id/send_message")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Send Image POST https://api.meetstream.ai/api/v1/bots/{bot_id}/send_image Content-Type: application/json Reference: https://docs.meetstream.ai/api-reference/api-endpoints/bot-endpoints/send-image ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/bots/{bot_id}/send_image: post: operationId: send-image summary: Send Image tags: - subpackage_bots parameters: - name: bot_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/SendImageResponse' requestBody: content: application/json: schema: $ref: '#/components/schemas/SendImageRequest' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: SendImageRequestMetadata: type: object properties: message_type: type: string title: SendImageRequestMetadata SendImageRequest: type: object properties: img_url: type: string display_duration: type: integer metadata: $ref: '#/components/schemas/SendImageRequestMetadata' required: - img_url title: SendImageRequest SendImageResponse: type: object properties: status: type: string bot_id: type: string command: type: string title: SendImageResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Request** ```json { "img_url": "https://media1.tenor.com/m/DgzBbRG6ibUAAAAC/gross-eek.gif" } ``` **Response** ```json { "status": "accepted", "bot_id": "526b6061-220f-47a9-972c-134b528c91e2", "command": "sendimg_url" } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/bots/bot_id/send_image" payload = { "img_url": "https://media1.tenor.com/m/DgzBbRG6ibUAAAAC/gross-eek.gif" } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/bots/bot_id/send_image'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"img_url":"https://media1.tenor.com/m/DgzBbRG6ibUAAAAC/gross-eek.gif"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/bot_id/send_image" payload := strings.NewReader("{\n \"img_url\": \"https://media1.tenor.com/m/DgzBbRG6ibUAAAAC/gross-eek.gif\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/bot_id/send_image") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"img_url\": \"https://media1.tenor.com/m/DgzBbRG6ibUAAAAC/gross-eek.gif\"\n}" response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/bot_id/send_image") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"img_url\": \"https://media1.tenor.com/m/DgzBbRG6ibUAAAAC/gross-eek.gif\"\n}") .asString(); ``` ```php request('POST', 'https://api.meetstream.ai/api/v1/bots/bot_id/send_image', [ 'body' => '{ "img_url": "https://media1.tenor.com/m/DgzBbRG6ibUAAAAC/gross-eek.gif" }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/bot_id/send_image"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"img_url\": \"https://media1.tenor.com/m/DgzBbRG6ibUAAAAC/gross-eek.gif\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = ["img_url": "https://media1.tenor.com/m/DgzBbRG6ibUAAAAC/gross-eek.gif"] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/bot_id/send_image")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Get Transcription GET https://api.meetstream.ai/api/v1/transcript/{transcript_id}/get_transcript Reference: https://docs.meetstream.ai/api-reference/api-endpoints/transcription/get-transcription ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/transcript/{transcript_id}/get_transcript: get: operationId: get-transcription summary: Get Transcription tags: - subpackage_transcription parameters: - name: transcript_id in: path required: true schema: type: string - name: raw in: query description: >- Set to true to retrieve the raw unformatted transcript from the provider. required: false schema: type: boolean - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: >- #/components/schemas/Transcription_getTranscription_Response_200 servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: GetTranscriptionSchemaItemsWordsItems: type: object properties: word: type: string punctuated_word: type: string start: type: number format: double end: type: number format: double confidence: type: number format: double speaker: type: integer speaker_confidence: type: number format: double title: GetTranscriptionSchemaItemsWordsItems GetTranscriptionSchemaItems: type: object properties: speaker: type: string transcript: type: string start_time: type: number format: double end_time: type: number format: double absolute_start_time: type: string absolute_end_time: type: string words: type: array items: $ref: '#/components/schemas/GetTranscriptionSchemaItemsWordsItems' title: GetTranscriptionSchemaItems GetTranscriptionSchema: type: array items: $ref: '#/components/schemas/GetTranscriptionSchemaItems' title: GetTranscriptionSchema GetRawTranscriptionSchemaWordsItems: type: object properties: text: type: string start: type: integer end: type: integer confidence: type: number format: double speaker: type: string title: GetRawTranscriptionSchemaWordsItems GetRawTranscriptionSchema: type: object properties: id: type: string status: type: string text: type: string language_code: type: string audio_url: type: string confidence: type: number format: double audio_duration: type: integer speaker_labels: type: boolean words: type: array items: $ref: '#/components/schemas/GetRawTranscriptionSchemaWordsItems' utterances: type: array items: type: object additionalProperties: description: Any type title: GetRawTranscriptionSchema Transcription_getTranscription_Response_200: oneOf: - $ref: '#/components/schemas/GetTranscriptionSchema' - $ref: '#/components/schemas/GetRawTranscriptionSchema' title: Transcription_getTranscription_Response_200 securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples ### Get Transcription **Response** ```json [ { "speaker": "Maddy Singh", "transcript": "There.", "start_time": 0.08, "end_time": 0.32, "absolute_start_time": "1970-01-01T00:18:23.429143", "absolute_end_time": "1970-01-01T00:18:23.669143", "words": [ { "word": "There.", "punctuated_word": "There.", "start": 0.08, "end": 0.32, "confidence": 0.58496094, "speaker": 0, "speaker_confidence": 1 } ] } ] ``` **SDK Code** ```python Get Transcription import requests url = "https://api.meetstream.ai/api/v1/transcript/aa6e41aa-a21b-45ff-8f4d-40226c9eaea8/get_transcript" headers = {"Authorization": ""} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript Get Transcription const url = 'https://api.meetstream.ai/api/v1/transcript/aa6e41aa-a21b-45ff-8f4d-40226c9eaea8/get_transcript'; const options = {method: 'GET', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Get Transcription package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/transcript/aa6e41aa-a21b-45ff-8f4d-40226c9eaea8/get_transcript" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Get Transcription require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/transcript/aa6e41aa-a21b-45ff-8f4d-40226c9eaea8/get_transcript") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java Get Transcription import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.meetstream.ai/api/v1/transcript/aa6e41aa-a21b-45ff-8f4d-40226c9eaea8/get_transcript") .header("Authorization", "") .asString(); ``` ```php Get Transcription request('GET', 'https://api.meetstream.ai/api/v1/transcript/aa6e41aa-a21b-45ff-8f4d-40226c9eaea8/get_transcript', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp Get Transcription using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/transcript/aa6e41aa-a21b-45ff-8f4d-40226c9eaea8/get_transcript"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift Get Transcription import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/transcript/aa6e41aa-a21b-45ff-8f4d-40226c9eaea8/get_transcript")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Get Raw Transcription **Response** ```json { "id": "a63395be-d812-4dab-9e6d-36f4dc188e44", "status": "completed", "text": "It. Hello? Hello? 1, 2, 3. 1, 2, 3. Mic testing.", "language_code": "en_us", "audio_url": "https://cdn.assemblyai.com/upload/audio-file", "confidence": 0.95929843, "audio_duration": 119, "speaker_labels": true, "words": [ { "text": "It.", "start": 160, "end": 400, "confidence": 0.90527344, "speaker": "A" } ] } ``` **SDK Code** ```python Get Raw Transcription import requests url = "https://api.meetstream.ai/api/v1/transcript/aa6e41aa-a21b-45ff-8f4d-40226c9eaea8/get_transcript" querystring = {"raw":"true"} headers = {"Authorization": ""} response = requests.get(url, headers=headers, params=querystring) print(response.json()) ``` ```javascript Get Raw Transcription const url = 'https://api.meetstream.ai/api/v1/transcript/aa6e41aa-a21b-45ff-8f4d-40226c9eaea8/get_transcript?raw=true'; const options = {method: 'GET', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Get Raw Transcription package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/transcript/aa6e41aa-a21b-45ff-8f4d-40226c9eaea8/get_transcript?raw=true" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Get Raw Transcription require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/transcript/aa6e41aa-a21b-45ff-8f4d-40226c9eaea8/get_transcript?raw=true") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java Get Raw Transcription import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.meetstream.ai/api/v1/transcript/aa6e41aa-a21b-45ff-8f4d-40226c9eaea8/get_transcript?raw=true") .header("Authorization", "") .asString(); ``` ```php Get Raw Transcription request('GET', 'https://api.meetstream.ai/api/v1/transcript/aa6e41aa-a21b-45ff-8f4d-40226c9eaea8/get_transcript?raw=true', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp Get Raw Transcription using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/transcript/aa6e41aa-a21b-45ff-8f4d-40226c9eaea8/get_transcript?raw=true"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift Get Raw Transcription import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/transcript/aa6e41aa-a21b-45ff-8f4d-40226c9eaea8/get_transcript?raw=true")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Transcribe POST https://api.meetstream.ai/api/v1/bots/{bot_id}/transcribe Content-Type: application/json Reference: https://docs.meetstream.ai/api-reference/api-endpoints/transcription/transcribe-bot-audio ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/bots/{bot_id}/transcribe: post: operationId: transcribe-bot-audio summary: Transcribe tags: - subpackage_transcription parameters: - name: bot_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/TranscribeResponse' requestBody: content: application/json: schema: $ref: '#/components/schemas/TranscribeRequest' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: TranscribeRequestProviderDeepgramModel: type: string enum: - nova-3 title: TranscribeRequestProviderDeepgramModel TranscribeRequestProviderDeepgram: type: object properties: model: $ref: '#/components/schemas/TranscribeRequestProviderDeepgramModel' language: type: string punctuate: type: boolean smart_format: type: boolean diarize: type: boolean paragraphs: type: boolean numerals: type: boolean filler_words: type: boolean keywords: type: array items: type: string utterances: type: boolean utt_split: type: number format: double detect_language: type: boolean search: type: array items: type: string tag: type: array items: type: string title: TranscribeRequestProviderDeepgram TranscribeRequestProviderMeetstream: type: object properties: language: type: string translate: type: boolean title: TranscribeRequestProviderMeetstream TranscribeRequestProviderJigsawstack: type: object properties: language: type: string translate: type: boolean by_speaker: type: boolean title: TranscribeRequestProviderJigsawstack TranscribeRequestProviderSarvamModel: type: string enum: - saaras:v3 title: TranscribeRequestProviderSarvamModel TranscribeRequestProviderSarvamMode: type: string enum: - transcribe - translate title: TranscribeRequestProviderSarvamMode TranscribeRequestProviderSarvam: type: object properties: model: $ref: '#/components/schemas/TranscribeRequestProviderSarvamModel' language_code: type: string mode: $ref: '#/components/schemas/TranscribeRequestProviderSarvamMode' with_diarization: type: boolean title: TranscribeRequestProviderSarvam TranscribeRequestProviderAssemblyaiSpeechModelsItems: type: string enum: - universal-2 title: TranscribeRequestProviderAssemblyaiSpeechModelsItems TranscribeRequestProviderAssemblyai: type: object properties: speech_models: type: array items: $ref: >- #/components/schemas/TranscribeRequestProviderAssemblyaiSpeechModelsItems language_code: type: string speaker_labels: type: boolean punctuate: type: boolean format_text: type: boolean filter_profanity: type: boolean redact_pii: type: boolean auto_chapters: type: boolean entity_detection: type: boolean keyterms_prompt: type: array items: type: string title: TranscribeRequestProviderAssemblyai TranscribeRequestProvider: type: object properties: deepgram: $ref: '#/components/schemas/TranscribeRequestProviderDeepgram' meetstream: $ref: '#/components/schemas/TranscribeRequestProviderMeetstream' jigsawstack: $ref: '#/components/schemas/TranscribeRequestProviderJigsawstack' sarvam: $ref: '#/components/schemas/TranscribeRequestProviderSarvam' assemblyai: $ref: '#/components/schemas/TranscribeRequestProviderAssemblyai' title: TranscribeRequestProvider TranscribeRequest: type: object properties: provider: $ref: '#/components/schemas/TranscribeRequestProvider' callback_url: type: string required: - provider title: TranscribeRequest TranscribeResponse: type: object properties: bot_id: type: string transcript_id: type: string provider: type: string message: type: string title: TranscribeResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples ### Deepgram Transcribe **Request** ```json { "provider": { "deepgram": { "model": "nova-3", "language": "en", "punctuate": true, "smart_format": true, "diarize": true, "paragraphs": true, "numerals": true, "filler_words": false, "keywords": [ "MeetStream", "recording", "transcript" ], "utterances": true, "utt_split": 0.8, "detect_language": false, "search": [ "MeetStream", "recording" ], "tag": [ "custom" ] } }, "callback_url": "https://your-server.com" } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": "dca2d0d4-5526-47a4-8194-55d64dd88049", "provider": "deepgram", "message": "Transcription processing started" } ``` **SDK Code** ```python Deepgram Transcribe import requests url = "https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe" payload = { "provider": { "deepgram": { "model": "nova-3", "language": "en", "punctuate": True, "smart_format": True, "diarize": True, "paragraphs": True, "numerals": True, "filler_words": False, "keywords": ["MeetStream", "recording", "transcript"], "utterances": True, "utt_split": 0.8, "detect_language": False, "search": ["MeetStream", "recording"], "tag": ["custom"] } }, "callback_url": "https://your-server.com" } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Deepgram Transcribe const url = 'https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"provider":{"deepgram":{"model":"nova-3","language":"en","punctuate":true,"smart_format":true,"diarize":true,"paragraphs":true,"numerals":true,"filler_words":false,"keywords":["MeetStream","recording","transcript"],"utterances":true,"utt_split":0.8,"detect_language":false,"search":["MeetStream","recording"],"tag":["custom"]}},"callback_url":"https://your-server.com"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Deepgram Transcribe package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe" payload := strings.NewReader("{\n \"provider\": {\n \"deepgram\": {\n \"model\": \"nova-3\",\n \"language\": \"en\",\n \"punctuate\": true,\n \"smart_format\": true,\n \"diarize\": true,\n \"paragraphs\": true,\n \"numerals\": true,\n \"filler_words\": false,\n \"keywords\": [\n \"MeetStream\",\n \"recording\",\n \"transcript\"\n ],\n \"utterances\": true,\n \"utt_split\": 0.8,\n \"detect_language\": false,\n \"search\": [\n \"MeetStream\",\n \"recording\"\n ],\n \"tag\": [\n \"custom\"\n ]\n }\n },\n \"callback_url\": \"https://your-server.com\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Deepgram Transcribe require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"provider\": {\n \"deepgram\": {\n \"model\": \"nova-3\",\n \"language\": \"en\",\n \"punctuate\": true,\n \"smart_format\": true,\n \"diarize\": true,\n \"paragraphs\": true,\n \"numerals\": true,\n \"filler_words\": false,\n \"keywords\": [\n \"MeetStream\",\n \"recording\",\n \"transcript\"\n ],\n \"utterances\": true,\n \"utt_split\": 0.8,\n \"detect_language\": false,\n \"search\": [\n \"MeetStream\",\n \"recording\"\n ],\n \"tag\": [\n \"custom\"\n ]\n }\n },\n \"callback_url\": \"https://your-server.com\"\n}" response = http.request(request) puts response.read_body ``` ```java Deepgram Transcribe import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"provider\": {\n \"deepgram\": {\n \"model\": \"nova-3\",\n \"language\": \"en\",\n \"punctuate\": true,\n \"smart_format\": true,\n \"diarize\": true,\n \"paragraphs\": true,\n \"numerals\": true,\n \"filler_words\": false,\n \"keywords\": [\n \"MeetStream\",\n \"recording\",\n \"transcript\"\n ],\n \"utterances\": true,\n \"utt_split\": 0.8,\n \"detect_language\": false,\n \"search\": [\n \"MeetStream\",\n \"recording\"\n ],\n \"tag\": [\n \"custom\"\n ]\n }\n },\n \"callback_url\": \"https://your-server.com\"\n}") .asString(); ``` ```php Deepgram Transcribe request('POST', 'https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe', [ 'body' => '{ "provider": { "deepgram": { "model": "nova-3", "language": "en", "punctuate": true, "smart_format": true, "diarize": true, "paragraphs": true, "numerals": true, "filler_words": false, "keywords": [ "MeetStream", "recording", "transcript" ], "utterances": true, "utt_split": 0.8, "detect_language": false, "search": [ "MeetStream", "recording" ], "tag": [ "custom" ] } }, "callback_url": "https://your-server.com" }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Deepgram Transcribe using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"provider\": {\n \"deepgram\": {\n \"model\": \"nova-3\",\n \"language\": \"en\",\n \"punctuate\": true,\n \"smart_format\": true,\n \"diarize\": true,\n \"paragraphs\": true,\n \"numerals\": true,\n \"filler_words\": false,\n \"keywords\": [\n \"MeetStream\",\n \"recording\",\n \"transcript\"\n ],\n \"utterances\": true,\n \"utt_split\": 0.8,\n \"detect_language\": false,\n \"search\": [\n \"MeetStream\",\n \"recording\"\n ],\n \"tag\": [\n \"custom\"\n ]\n }\n },\n \"callback_url\": \"https://your-server.com\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Deepgram Transcribe import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "provider": ["deepgram": [ "model": "nova-3", "language": "en", "punctuate": true, "smart_format": true, "diarize": true, "paragraphs": true, "numerals": true, "filler_words": false, "keywords": ["MeetStream", "recording", "transcript"], "utterances": true, "utt_split": 0.8, "detect_language": false, "search": ["MeetStream", "recording"], "tag": ["custom"] ]], "callback_url": "https://your-server.com" ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### MIA Transcribe **Request** ```json { "provider": { "meetstream": { "language": "auto", "translate": false } }, "callback_url": "https://your-server.com" } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": "dca2d0d4-5526-47a4-8194-55d64dd88049", "provider": "meetstream", "message": "Transcription processing started" } ``` **SDK Code** ```python MIA Transcribe import requests url = "https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe" payload = { "provider": { "meetstream": { "language": "auto", "translate": False } }, "callback_url": "https://your-server.com" } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript MIA Transcribe const url = 'https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"provider":{"meetstream":{"language":"auto","translate":false}},"callback_url":"https://your-server.com"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go MIA Transcribe package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe" payload := strings.NewReader("{\n \"provider\": {\n \"meetstream\": {\n \"language\": \"auto\",\n \"translate\": false\n }\n },\n \"callback_url\": \"https://your-server.com\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby MIA Transcribe require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"provider\": {\n \"meetstream\": {\n \"language\": \"auto\",\n \"translate\": false\n }\n },\n \"callback_url\": \"https://your-server.com\"\n}" response = http.request(request) puts response.read_body ``` ```java MIA Transcribe import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"provider\": {\n \"meetstream\": {\n \"language\": \"auto\",\n \"translate\": false\n }\n },\n \"callback_url\": \"https://your-server.com\"\n}") .asString(); ``` ```php MIA Transcribe request('POST', 'https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe', [ 'body' => '{ "provider": { "meetstream": { "language": "auto", "translate": false } }, "callback_url": "https://your-server.com" }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp MIA Transcribe using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"provider\": {\n \"meetstream\": {\n \"language\": \"auto\",\n \"translate\": false\n }\n },\n \"callback_url\": \"https://your-server.com\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift MIA Transcribe import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "provider": ["meetstream": [ "language": "auto", "translate": false ]], "callback_url": "https://your-server.com" ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Jigsaw Transcribe **Request** ```json { "provider": { "jigsawstack": { "language": "auto", "translate": false, "by_speaker": true } }, "callback_url": "https://your-server.com" } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": "dca2d0d4-5526-47a4-8194-55d64dd88049", "provider": "jigsaw", "message": "Transcription processing started" } ``` **SDK Code** ```python Jigsaw Transcribe import requests url = "https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe" payload = { "provider": { "jigsawstack": { "language": "auto", "translate": False, "by_speaker": True } }, "callback_url": "https://your-server.com" } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Jigsaw Transcribe const url = 'https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"provider":{"jigsawstack":{"language":"auto","translate":false,"by_speaker":true}},"callback_url":"https://your-server.com"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Jigsaw Transcribe package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe" payload := strings.NewReader("{\n \"provider\": {\n \"jigsawstack\": {\n \"language\": \"auto\",\n \"translate\": false,\n \"by_speaker\": true\n }\n },\n \"callback_url\": \"https://your-server.com\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Jigsaw Transcribe require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"provider\": {\n \"jigsawstack\": {\n \"language\": \"auto\",\n \"translate\": false,\n \"by_speaker\": true\n }\n },\n \"callback_url\": \"https://your-server.com\"\n}" response = http.request(request) puts response.read_body ``` ```java Jigsaw Transcribe import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"provider\": {\n \"jigsawstack\": {\n \"language\": \"auto\",\n \"translate\": false,\n \"by_speaker\": true\n }\n },\n \"callback_url\": \"https://your-server.com\"\n}") .asString(); ``` ```php Jigsaw Transcribe request('POST', 'https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe', [ 'body' => '{ "provider": { "jigsawstack": { "language": "auto", "translate": false, "by_speaker": true } }, "callback_url": "https://your-server.com" }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Jigsaw Transcribe using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"provider\": {\n \"jigsawstack\": {\n \"language\": \"auto\",\n \"translate\": false,\n \"by_speaker\": true\n }\n },\n \"callback_url\": \"https://your-server.com\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Jigsaw Transcribe import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "provider": ["jigsawstack": [ "language": "auto", "translate": false, "by_speaker": true ]], "callback_url": "https://your-server.com" ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Sarvam Transcribe **Request** ```json { "provider": { "sarvam": { "model": "saaras:v3", "language_code": "en-IN", "mode": "transcribe", "with_diarization": true } }, "callback_url": "https://your-server.com" } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": "dca2d0d4-5526-47a4-8194-55d64dd88049", "provider": "sarvam", "message": "Transcription processing started" } ``` **SDK Code** ```python Sarvam Transcribe import requests url = "https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe" payload = { "provider": { "sarvam": { "model": "saaras:v3", "language_code": "en-IN", "mode": "transcribe", "with_diarization": True } }, "callback_url": "https://your-server.com" } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Sarvam Transcribe const url = 'https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"provider":{"sarvam":{"model":"saaras:v3","language_code":"en-IN","mode":"transcribe","with_diarization":true}},"callback_url":"https://your-server.com"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Sarvam Transcribe package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe" payload := strings.NewReader("{\n \"provider\": {\n \"sarvam\": {\n \"model\": \"saaras:v3\",\n \"language_code\": \"en-IN\",\n \"mode\": \"transcribe\",\n \"with_diarization\": true\n }\n },\n \"callback_url\": \"https://your-server.com\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Sarvam Transcribe require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"provider\": {\n \"sarvam\": {\n \"model\": \"saaras:v3\",\n \"language_code\": \"en-IN\",\n \"mode\": \"transcribe\",\n \"with_diarization\": true\n }\n },\n \"callback_url\": \"https://your-server.com\"\n}" response = http.request(request) puts response.read_body ``` ```java Sarvam Transcribe import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"provider\": {\n \"sarvam\": {\n \"model\": \"saaras:v3\",\n \"language_code\": \"en-IN\",\n \"mode\": \"transcribe\",\n \"with_diarization\": true\n }\n },\n \"callback_url\": \"https://your-server.com\"\n}") .asString(); ``` ```php Sarvam Transcribe request('POST', 'https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe', [ 'body' => '{ "provider": { "sarvam": { "model": "saaras:v3", "language_code": "en-IN", "mode": "transcribe", "with_diarization": true } }, "callback_url": "https://your-server.com" }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Sarvam Transcribe using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"provider\": {\n \"sarvam\": {\n \"model\": \"saaras:v3\",\n \"language_code\": \"en-IN\",\n \"mode\": \"transcribe\",\n \"with_diarization\": true\n }\n },\n \"callback_url\": \"https://your-server.com\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Sarvam Transcribe import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "provider": ["sarvam": [ "model": "saaras:v3", "language_code": "en-IN", "mode": "transcribe", "with_diarization": true ]], "callback_url": "https://your-server.com" ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Assembly AI Transcribe **Request** ```json { "provider": { "assemblyai": { "speech_models": [ "universal-2" ], "language_code": "en_us", "speaker_labels": true, "punctuate": true, "format_text": true, "filter_profanity": false, "redact_pii": false, "auto_chapters": false, "entity_detection": false, "keyterms_prompt": [ "MeetStream" ] } }, "callback_url": "https://your-server.com" } ``` **Response** ```json { "bot_id": "604c0a0b-973d-4eb1-a5bf-2c7513f6bbe0", "transcript_id": "dca2d0d4-5526-47a4-8194-55d64dd88049", "provider": "assemblyai", "message": "Transcription processing started" } ``` **SDK Code** ```python Assembly AI Transcribe import requests url = "https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe" payload = { "provider": { "assemblyai": { "speech_models": ["universal-2"], "language_code": "en_us", "speaker_labels": True, "punctuate": True, "format_text": True, "filter_profanity": False, "redact_pii": False, "auto_chapters": False, "entity_detection": False, "keyterms_prompt": ["MeetStream"] } }, "callback_url": "https://your-server.com" } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Assembly AI Transcribe const url = 'https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"provider":{"assemblyai":{"speech_models":["universal-2"],"language_code":"en_us","speaker_labels":true,"punctuate":true,"format_text":true,"filter_profanity":false,"redact_pii":false,"auto_chapters":false,"entity_detection":false,"keyterms_prompt":["MeetStream"]}},"callback_url":"https://your-server.com"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Assembly AI Transcribe package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe" payload := strings.NewReader("{\n \"provider\": {\n \"assemblyai\": {\n \"speech_models\": [\n \"universal-2\"\n ],\n \"language_code\": \"en_us\",\n \"speaker_labels\": true,\n \"punctuate\": true,\n \"format_text\": true,\n \"filter_profanity\": false,\n \"redact_pii\": false,\n \"auto_chapters\": false,\n \"entity_detection\": false,\n \"keyterms_prompt\": [\n \"MeetStream\"\n ]\n }\n },\n \"callback_url\": \"https://your-server.com\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Assembly AI Transcribe require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"provider\": {\n \"assemblyai\": {\n \"speech_models\": [\n \"universal-2\"\n ],\n \"language_code\": \"en_us\",\n \"speaker_labels\": true,\n \"punctuate\": true,\n \"format_text\": true,\n \"filter_profanity\": false,\n \"redact_pii\": false,\n \"auto_chapters\": false,\n \"entity_detection\": false,\n \"keyterms_prompt\": [\n \"MeetStream\"\n ]\n }\n },\n \"callback_url\": \"https://your-server.com\"\n}" response = http.request(request) puts response.read_body ``` ```java Assembly AI Transcribe import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"provider\": {\n \"assemblyai\": {\n \"speech_models\": [\n \"universal-2\"\n ],\n \"language_code\": \"en_us\",\n \"speaker_labels\": true,\n \"punctuate\": true,\n \"format_text\": true,\n \"filter_profanity\": false,\n \"redact_pii\": false,\n \"auto_chapters\": false,\n \"entity_detection\": false,\n \"keyterms_prompt\": [\n \"MeetStream\"\n ]\n }\n },\n \"callback_url\": \"https://your-server.com\"\n}") .asString(); ``` ```php Assembly AI Transcribe request('POST', 'https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe', [ 'body' => '{ "provider": { "assemblyai": { "speech_models": [ "universal-2" ], "language_code": "en_us", "speaker_labels": true, "punctuate": true, "format_text": true, "filter_profanity": false, "redact_pii": false, "auto_chapters": false, "entity_detection": false, "keyterms_prompt": [ "MeetStream" ] } }, "callback_url": "https://your-server.com" }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Assembly AI Transcribe using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"provider\": {\n \"assemblyai\": {\n \"speech_models\": [\n \"universal-2\"\n ],\n \"language_code\": \"en_us\",\n \"speaker_labels\": true,\n \"punctuate\": true,\n \"format_text\": true,\n \"filter_profanity\": false,\n \"redact_pii\": false,\n \"auto_chapters\": false,\n \"entity_detection\": false,\n \"keyterms_prompt\": [\n \"MeetStream\"\n ]\n }\n },\n \"callback_url\": \"https://your-server.com\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Assembly AI Transcribe import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "provider": ["assemblyai": [ "speech_models": ["universal-2"], "language_code": "en_us", "speaker_labels": true, "punctuate": true, "format_text": true, "filter_profanity": false, "redact_pii": false, "auto_chapters": false, "entity_detection": false, "keyterms_prompt": ["MeetStream"] ]], "callback_url": "https://your-server.com" ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/526b6061-220f-47a9-972c-134b528c91e2/transcribe")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Transcriptions GET https://api.meetstream.ai/api/v1/bots/{bot_id}/transcriptions Reference: https://docs.meetstream.ai/api-reference/api-endpoints/transcription/get-bot-transcriptions ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/bots/{bot_id}/transcriptions: get: operationId: get-bot-transcriptions summary: Transcriptions tags: - subpackage_transcription parameters: - name: bot_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/BotTranscriptionsResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: BotTranscriptionsResponseTranscriptionsItemsStatus: type: string enum: - Success - Processing - Failed title: BotTranscriptionsResponseTranscriptionsItemsStatus BotTranscriptionsResponseTranscriptionsItemsDownloadUrls: type: object properties: raw_transcript: type: string description: Pre-signed S3 URL for the raw transcript. Valid for 1 hour. processed_transcript: type: string description: Pre-signed S3 URL for the processed transcript. Valid for 1 hour. title: BotTranscriptionsResponseTranscriptionsItemsDownloadUrls BotTranscriptionsResponseTranscriptionsItems: type: object properties: transcript_id: type: string provider: type: string status: $ref: >- #/components/schemas/BotTranscriptionsResponseTranscriptionsItemsStatus created_at: type: string config: type: object additionalProperties: description: Any type download_urls: oneOf: - $ref: >- #/components/schemas/BotTranscriptionsResponseTranscriptionsItemsDownloadUrls - type: 'null' title: BotTranscriptionsResponseTranscriptionsItems BotTranscriptionsResponse: type: object properties: bot_id: type: string transcriptions: type: array items: $ref: '#/components/schemas/BotTranscriptionsResponseTranscriptionsItems' title: BotTranscriptionsResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "bot_id": "0c0f8353-ad37-43af-ac53-322f23d44743", "transcriptions": [ { "transcript_id": "55020af8-2533-4675-ade0-4ad52f69d74e", "provider": "deepgram", "status": "Success", "created_at": "2026-04-06T23:17:55.992764+00:00", "config": {}, "download_urls": { "raw_transcript": "https://ms-s3-bucket.s3.amazonaws.com/raw_transcript.json", "processed_transcript": "https://ms-s3-bucket.s3.amazonaws.com/processed_transcript.json" } } ] } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/bots/bot_id/transcriptions" headers = {"Authorization": ""} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/bots/bot_id/transcriptions'; const options = {method: 'GET', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/bots/bot_id/transcriptions" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/bots/bot_id/transcriptions") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.meetstream.ai/api/v1/bots/bot_id/transcriptions") .header("Authorization", "") .asString(); ``` ```php request('GET', 'https://api.meetstream.ai/api/v1/bots/bot_id/transcriptions', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/bots/bot_id/transcriptions"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/bots/bot_id/transcriptions")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Create Calendar POST https://api.meetstream.ai/api/v1/calendar/create_calendar Content-Type: application/json Reference: https://docs.meetstream.ai/api-reference/api-endpoints/calendar/create-calendar ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/calendar/create_calendar: post: operationId: create-calendar summary: Create Calendar tags: - subpackage_calendar parameters: - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/CreateCalendarResponse' requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarCreateRequest' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: CalendarCreateRequest: type: object properties: google_client_id: type: string google_client_secret: type: string google_refresh_token: type: string required: - google_client_id - google_client_secret - google_refresh_token title: CalendarCreateRequest CreateCalendarResponseCalendarsItems: type: object properties: id: type: string summary: type: string description: type: - string - 'null' isPrimary: type: boolean accessRole: type: string timeZone: type: string backgroundColor: type: string foregroundColor: type: string selected: type: boolean title: CreateCalendarResponseCalendarsItems CreateCalendarResponseWatchSetupFailedCalendarsItems: type: object properties: {} title: CreateCalendarResponseWatchSetupFailedCalendarsItems CreateCalendarResponseWatchSetupWatchResultsItems: type: object properties: calendar_id: type: string calendar_summary: type: string channel_id: type: string expiration: type: string title: CreateCalendarResponseWatchSetupWatchResultsItems CreateCalendarResponseWatchSetup: type: object properties: success: type: boolean status: type: string watches_setup: type: integer message: type: string failed_calendars: type: array items: $ref: >- #/components/schemas/CreateCalendarResponseWatchSetupFailedCalendarsItems watch_results: type: array items: $ref: >- #/components/schemas/CreateCalendarResponseWatchSetupWatchResultsItems title: CreateCalendarResponseWatchSetup CreateCalendarResponse: type: object properties: calendar_id: type: string platform: type: string user_email: type: string user_name: type: string primary_calendar_id: type: string message: type: string calendars: type: array items: $ref: '#/components/schemas/CreateCalendarResponseCalendarsItems' watch_setup: $ref: '#/components/schemas/CreateCalendarResponseWatchSetup' title: CreateCalendarResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Request** ```json { "google_client_id": "string", "google_client_secret": "string", "google_refresh_token": "string" } ``` **Response** ```json { "calendar_id": "google_calendar_b528cc3e-3949-487d-aa9c-5e51893da2e2", "platform": "google_calendar", "user_email": "unknown", "user_name": "unknown", "primary_calendar_id": "sujoy.dutta410@gmail.com", "message": "Calendar connected successfully. Use /calendar/events and /calendar/schedule endpoints to manage events and bots.", "calendars": [ { "id": "sujoy.dutta410@gmail.com", "summary": "sujoy.dutta410@gmail.com", "description": "string", "isPrimary": true, "accessRole": "owner", "timeZone": "UTC", "backgroundColor": "#9fe1e7", "foregroundColor": "#000000", "selected": true } ], "watch_setup": { "success": true, "status": "completed", "watches_setup": 1, "message": "Watch channels set up successfully for 1 calendar(s)", "failed_calendars": [ {} ], "watch_results": [ { "calendar_id": "sujoy.dutta410@gmail.com", "calendar_summary": "sujoy.dutta410@gmail.com", "channel_id": "560fdce6-72b3-4b44-9282-f03db242d2bd", "expiration": "1776358891000" } ] } } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/calendar/create_calendar" payload = { "google_client_id": "string", "google_client_secret": "string", "google_refresh_token": "string" } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/calendar/create_calendar'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"google_client_id":"string","google_client_secret":"string","google_refresh_token":"string"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/calendar/create_calendar" payload := strings.NewReader("{\n \"google_client_id\": \"string\",\n \"google_client_secret\": \"string\",\n \"google_refresh_token\": \"string\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/calendar/create_calendar") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"google_client_id\": \"string\",\n \"google_client_secret\": \"string\",\n \"google_refresh_token\": \"string\"\n}" response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/calendar/create_calendar") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"google_client_id\": \"string\",\n \"google_client_secret\": \"string\",\n \"google_refresh_token\": \"string\"\n}") .asString(); ``` ```php request('POST', 'https://api.meetstream.ai/api/v1/calendar/create_calendar', [ 'body' => '{ "google_client_id": "string", "google_client_secret": "string", "google_refresh_token": "string" }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/calendar/create_calendar"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"google_client_id\": \"string\",\n \"google_client_secret\": \"string\",\n \"google_refresh_token\": \"string\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "google_client_id": "string", "google_client_secret": "string", "google_refresh_token": "string" ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/calendar/create_calendar")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Disconnect Calendar POST https://api.meetstream.ai/api/v1/calendar/disconnect Content-Type: application/json Reference: https://docs.meetstream.ai/api-reference/api-endpoints/calendar/disconnect-calendar ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/calendar/disconnect: post: operationId: disconnect-calendar summary: Disconnect Calendar tags: - subpackage_calendar parameters: - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/Calendar_disconnectCalendar_Response_200' requestBody: content: application/json: schema: $ref: '#/components/schemas/CalendarCreateRequest' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: CalendarCreateRequest: type: object properties: google_client_id: type: string google_client_secret: type: string google_refresh_token: type: string required: - google_client_id - google_client_secret - google_refresh_token title: CalendarCreateRequest Calendar_disconnectCalendar_Response_200: type: object properties: {} description: Empty response body title: Calendar_disconnectCalendar_Response_200 securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Request** ```json { "google_client_id": "string", "google_client_secret": "string", "google_refresh_token": "string" } ``` **Response** ```json {} ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/calendar/disconnect" payload = { "google_client_id": "string", "google_client_secret": "string", "google_refresh_token": "string" } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/calendar/disconnect'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"google_client_id":"string","google_client_secret":"string","google_refresh_token":"string"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/calendar/disconnect" payload := strings.NewReader("{\n \"google_client_id\": \"string\",\n \"google_client_secret\": \"string\",\n \"google_refresh_token\": \"string\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/calendar/disconnect") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"google_client_id\": \"string\",\n \"google_client_secret\": \"string\",\n \"google_refresh_token\": \"string\"\n}" response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/calendar/disconnect") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"google_client_id\": \"string\",\n \"google_client_secret\": \"string\",\n \"google_refresh_token\": \"string\"\n}") .asString(); ``` ```php request('POST', 'https://api.meetstream.ai/api/v1/calendar/disconnect', [ 'body' => '{ "google_client_id": "string", "google_client_secret": "string", "google_refresh_token": "string" }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/calendar/disconnect"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"google_client_id\": \"string\",\n \"google_client_secret\": \"string\",\n \"google_refresh_token\": \"string\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "google_client_id": "string", "google_client_secret": "string", "google_refresh_token": "string" ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/calendar/disconnect")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Get Calendars GET https://api.meetstream.ai/api/v1/calendar Reference: https://docs.meetstream.ai/api-reference/api-endpoints/calendar/get-calendars ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/calendar: get: operationId: get-calendars summary: Get Calendars tags: - subpackage_calendar parameters: - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/GetCalendarsResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: GetCalendarsResponseCalendarsItems: type: object properties: id: type: string summary: type: string description: type: - string - 'null' isPrimary: type: boolean accessRole: type: string timeZone: type: string backgroundColor: type: string foregroundColor: type: string selected: type: boolean hidden: type: boolean title: GetCalendarsResponseCalendarsItems GetCalendarsResponse: type: object properties: total: type: integer user_id: type: string calendars: type: array items: $ref: '#/components/schemas/GetCalendarsResponseCalendarsItems' title: GetCalendarsResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "total": 2, "user_id": "34e86428-0081-70e8-5bfb-3fa111d0ff77", "calendars": [ { "id": "shreyaa@meetstream.ai", "summary": "shreyaa@meetstream.ai", "description": "string", "isPrimary": true, "accessRole": "owner", "timeZone": "Asia/Kolkata", "backgroundColor": "#9fe1e7", "foregroundColor": "#000000", "selected": true, "hidden": false } ] } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/calendar" headers = {"Authorization": ""} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/calendar'; const options = {method: 'GET', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/calendar" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/calendar") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.meetstream.ai/api/v1/calendar") .header("Authorization", "") .asString(); ``` ```php request('GET', 'https://api.meetstream.ai/api/v1/calendar', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/calendar"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/calendar")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Schedule Event POST https://api.meetstream.ai/api/v1/calendar/schedule/{event_id} Reference: https://docs.meetstream.ai/api-reference/api-endpoints/calendar/schedule-event ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/calendar/schedule/{event_id}: post: operationId: schedule-event summary: Schedule Event tags: - subpackage_calendar parameters: - name: event_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/ScheduleEventResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: ScheduleEventResponseBotConfig: type: object properties: bot_name: type: string bot_message: type: string bot_image_url: type: string audio_required: type: boolean video_required: type: boolean callback_url: type: string meeting_url: type: string join_at: type: string deduplication_key: type: string automatic_leave: type: object additionalProperties: description: Any type recording_config: type: object additionalProperties: description: Any type custom_attributes: type: object additionalProperties: description: Any type live_audio_required: type: object additionalProperties: description: Any type live_transcription_required: type: object additionalProperties: description: Any type title: ScheduleEventResponseBotConfig ScheduleEventResponse: type: object properties: scheduled: type: boolean schedule_id: type: string bot_id: type: string schedule_group: type: string event_id: type: string scheduled_time: type: string existing_schedules: type: integer occurrence_date: type: - string - 'null' is_recurring_occurrence: type: boolean bot_config: $ref: '#/components/schemas/ScheduleEventResponseBotConfig' title: ScheduleEventResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "scheduled": true, "schedule_id": "bot-945804c8-0834f3b9-043", "bot_id": "0834f3b9-043c-4496-9eb0-e869bd38ca1e", "schedule_group": "google_meet", "event_id": "evt_2a1179ef047a5285", "scheduled_time": "2026-03-24T15:57:00+00:00", "existing_schedules": 0, "occurrence_date": "string", "is_recurring_occurrence": false, "bot_config": { "bot_name": "Maddy's Agent", "bot_message": "MeetStream bot joining your meeting", "bot_image_url": "https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png", "audio_required": true, "video_required": true, "callback_url": "", "meeting_url": "https://meet.google.com/sfw-fjpa-syk", "join_at": "2026-03-24T09:00:00-07:00", "deduplication_key": "001009", "automatic_leave": {}, "recording_config": {}, "custom_attributes": {}, "live_audio_required": {}, "live_transcription_required": {} } } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/calendar/schedule/event_id" headers = {"Authorization": ""} response = requests.post(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/calendar/schedule/event_id'; const options = {method: 'POST', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/calendar/schedule/event_id" req, _ := http.NewRequest("POST", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/calendar/schedule/event_id") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/calendar/schedule/event_id") .header("Authorization", "") .asString(); ``` ```php request('POST', 'https://api.meetstream.ai/api/v1/calendar/schedule/event_id', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/calendar/schedule/event_id"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/calendar/schedule/event_id")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Remove Schedule Event DELETE https://api.meetstream.ai/api/v1/calendar/schedule/{event_id} Reference: https://docs.meetstream.ai/api-reference/api-endpoints/calendar/remove-schedule-event ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/calendar/schedule/{event_id}: delete: operationId: remove-schedule-event summary: Remove Schedule Event tags: - subpackage_calendar parameters: - name: event_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/Calendar_removeScheduleEvent_Response_200' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: Calendar_removeScheduleEvent_Response_200: type: object properties: {} description: Empty response body title: Calendar_removeScheduleEvent_Response_200 securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json {} ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/calendar/schedule/event_id" headers = {"Authorization": ""} response = requests.delete(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/calendar/schedule/event_id'; const options = {method: 'DELETE', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/calendar/schedule/event_id" req, _ := http.NewRequest("DELETE", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/calendar/schedule/event_id") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Delete.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.delete("https://api.meetstream.ai/api/v1/calendar/schedule/event_id") .header("Authorization", "") .asString(); ``` ```php request('DELETE', 'https://api.meetstream.ai/api/v1/calendar/schedule/event_id', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/calendar/schedule/event_id"); var request = new RestRequest(Method.DELETE); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/calendar/schedule/event_id")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "DELETE" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Fetch/Sync Events GET https://api.meetstream.ai/api/v1/calendar/events Reference: https://docs.meetstream.ai/api-reference/api-endpoints/calendar/fetch-sync-events ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/calendar/events: get: operationId: fetch-sync-events summary: Fetch/Sync Events tags: - subpackage_calendar parameters: - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/FetchEventsResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: FetchEventsResponseResultsItems: type: object properties: id: type: string start_time: type: string end_time: type: string calendar_id: type: string platform: type: string platform_id: type: string ical_uid: type: string meeting_platform: type: string meeting_url: type: string created_at: type: string updated_at: type: string is_deleted: type: boolean raw: type: object additionalProperties: description: Any type bots: type: array items: type: object additionalProperties: description: Any type title: FetchEventsResponseResultsItems FetchEventsResponse: type: object properties: next: type: - string - 'null' previous: type: - string - 'null' has_more: type: boolean results: type: array items: $ref: '#/components/schemas/FetchEventsResponseResultsItems' title: FetchEventsResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "next": "string", "previous": "string", "has_more": false, "results": [ { "id": "evt_9198b1f67bb94e7e", "start_time": "2026-03-20T22:00:00+05:30", "end_time": "2026-03-20T22:30:00+05:30", "calendar_id": "agent@meetstream.ai", "platform": "google_calendar", "platform_id": "6khchbgjpkasrqo4jllmb0l806_20260318T163000Z", "ical_uid": "6khchbgjpkasrqo4jllmb0l806_R20260318T163000@google.com", "meeting_platform": "google_meet", "meeting_url": "https://meet.google.com/zov-guod-aqb", "created_at": "2026-03-19T18:18:21Z", "updated_at": "2026-03-20T00:00:03Z", "is_deleted": false, "raw": {}, "bots": [ {} ] } ] } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/calendar/events" headers = {"Authorization": ""} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/calendar/events'; const options = {method: 'GET', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/calendar/events" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/calendar/events") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.meetstream.ai/api/v1/calendar/events") .header("Authorization", "") .asString(); ``` ```php request('GET', 'https://api.meetstream.ai/api/v1/calendar/events', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/calendar/events"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/calendar/events")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Toggle Recurring Event POST https://api.meetstream.ai/api/v1/calendar/toggle-recurrence Content-Type: application/json Reference: https://docs.meetstream.ai/api-reference/api-endpoints/calendar/toggle-recurring-event ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/calendar/toggle-recurrence: post: operationId: toggle-recurring-event summary: Toggle Recurring Event tags: - subpackage_calendar parameters: - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: >- #/components/schemas/Calendar_toggleRecurringEvent_Response_200 requestBody: content: application/json: schema: $ref: '#/components/schemas/ToggleRecurringEventRequest' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: ToggleRecurringEventRequest: type: object properties: event_id: type: string recurring_enabled: type: boolean required: - event_id - recurring_enabled title: ToggleRecurringEventRequest Calendar_toggleRecurringEvent_Response_200: type: object properties: {} description: Empty response body title: Calendar_toggleRecurringEvent_Response_200 securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Request** ```json { "event_id": "evt_2a1179ef047a5285", "recurring_enabled": false } ``` **Response** ```json {} ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/calendar/toggle-recurrence" payload = { "event_id": "evt_2a1179ef047a5285", "recurring_enabled": False } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/calendar/toggle-recurrence'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"event_id":"evt_2a1179ef047a5285","recurring_enabled":false}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/calendar/toggle-recurrence" payload := strings.NewReader("{\n \"event_id\": \"evt_2a1179ef047a5285\",\n \"recurring_enabled\": false\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/calendar/toggle-recurrence") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"event_id\": \"evt_2a1179ef047a5285\",\n \"recurring_enabled\": false\n}" response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/calendar/toggle-recurrence") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"event_id\": \"evt_2a1179ef047a5285\",\n \"recurring_enabled\": false\n}") .asString(); ``` ```php request('POST', 'https://api.meetstream.ai/api/v1/calendar/toggle-recurrence', [ 'body' => '{ "event_id": "evt_2a1179ef047a5285", "recurring_enabled": false }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/calendar/toggle-recurrence"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"event_id\": \"evt_2a1179ef047a5285\",\n \"recurring_enabled\": false\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "event_id": "evt_2a1179ef047a5285", "recurring_enabled": false ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/calendar/toggle-recurrence")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Setup Cron POST https://api.meetstream.ai/api/v1/calendar/auto-schedule/enable Content-Type: application/json Reference: https://docs.meetstream.ai/api-reference/api-endpoints/calendar/setup-cron ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/calendar/auto-schedule/enable: post: operationId: setup-cron summary: Setup Cron tags: - subpackage_calendar parameters: - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/SetupCronResponse' requestBody: content: application/json: schema: $ref: '#/components/schemas/SetupCronRequest' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: AutomaticLeaveConfig: type: object properties: waiting_room_timeout: type: integer description: |- Seconds to wait in a waiting room before leaving. (Default Value: 600 seconds / 10 min) everyone_left_timeout: type: integer description: >- Seconds to stay in the call after everyone else leaves. (Default Value: 300 seconds / 5 minutes) voice_inactivity_timeout: type: integer description: >- Seconds to wait if no audio is detected. (Default Value: 100 seconds / 1 minute and 40 seconds) in_call_recording_timeout: type: integer description: >- Maximum seconds to stay in call while recording. (Default Value: 14400 seconds / 4 hours) recording_permission_denied_timeout: type: integer description: >- Seconds to wait if recording permission is denied. This is specific to Zoom only. (Default Value: 60 seconds / 1 minute). description: >- Configure how long the bot should remain in the meeting under different conditions before leaving automatically. All timeout values are specified in seconds and accept integer values only. title: AutomaticLeaveConfig SetupCronRequestDefaultBotConfig: type: object properties: bot_name: type: string audio_required: type: boolean video_required: type: boolean bot_message: type: string bot_image_url: type: string automatic_leave: $ref: '#/components/schemas/AutomaticLeaveConfig' title: SetupCronRequestDefaultBotConfig SetupCronRequest: type: object properties: default_bot_config: $ref: '#/components/schemas/SetupCronRequestDefaultBotConfig' title: SetupCronRequest SetupCronResponseDefaultBotConfigAutomaticLeave: type: object properties: waiting_room_timeout: type: integer everyone_left_timeout: type: integer voice_inactivity_timeout: type: integer in_call_recording_timeout: type: integer recording_permission_denied_timeout: type: integer title: SetupCronResponseDefaultBotConfigAutomaticLeave SetupCronResponseDefaultBotConfig: type: object properties: bot_name: type: string audio_required: type: boolean video_required: type: boolean bot_message: type: string bot_image_url: type: string automatic_leave: $ref: '#/components/schemas/SetupCronResponseDefaultBotConfigAutomaticLeave' title: SetupCronResponseDefaultBotConfig SetupCronResponse: type: object properties: message: type: string auto_schedule_enabled: type: boolean default_bot_config: $ref: '#/components/schemas/SetupCronResponseDefaultBotConfig' title: SetupCronResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Request** ```json {} ``` **Response** ```json { "message": "Auto-scheduling enabled successfully", "auto_schedule_enabled": true, "default_bot_config": { "bot_name": "Maddy's Agent", "audio_required": true, "video_required": true, "bot_message": "Hey Everyone 👋", "bot_image_url": "https://blog.meetstream.ai/wp-content/uploads/2025/08/Zoom-BG-Image.png", "automatic_leave": { "waiting_room_timeout": 300, "everyone_left_timeout": 100, "voice_inactivity_timeout": 100, "in_call_recording_timeout": 14400, "recording_permission_denied_timeout": 60 } } } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/calendar/auto-schedule/enable" payload = {} headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/calendar/auto-schedule/enable'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/calendar/auto-schedule/enable" payload := strings.NewReader("{}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/calendar/auto-schedule/enable") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{}" response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/calendar/auto-schedule/enable") .header("Authorization", "") .header("Content-Type", "application/json") .body("{}") .asString(); ``` ```php request('POST', 'https://api.meetstream.ai/api/v1/calendar/auto-schedule/enable', [ 'body' => '{}', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/calendar/auto-schedule/enable"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/calendar/auto-schedule/enable")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Disable Cron POST https://api.meetstream.ai/api/v1/calendar/auto-schedule/disable Content-Type: application/json Reference: https://docs.meetstream.ai/api-reference/api-endpoints/calendar/disable-cron ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/calendar/auto-schedule/disable: post: operationId: disable-cron summary: Disable Cron tags: - subpackage_calendar parameters: - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/Calendar_disableCron_Response_200' requestBody: content: application/json: schema: $ref: '#/components/schemas/SetupCronRequest' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: AutomaticLeaveConfig: type: object properties: waiting_room_timeout: type: integer description: |- Seconds to wait in a waiting room before leaving. (Default Value: 600 seconds / 10 min) everyone_left_timeout: type: integer description: >- Seconds to stay in the call after everyone else leaves. (Default Value: 300 seconds / 5 minutes) voice_inactivity_timeout: type: integer description: >- Seconds to wait if no audio is detected. (Default Value: 100 seconds / 1 minute and 40 seconds) in_call_recording_timeout: type: integer description: >- Maximum seconds to stay in call while recording. (Default Value: 14400 seconds / 4 hours) recording_permission_denied_timeout: type: integer description: >- Seconds to wait if recording permission is denied. This is specific to Zoom only. (Default Value: 60 seconds / 1 minute). description: >- Configure how long the bot should remain in the meeting under different conditions before leaving automatically. All timeout values are specified in seconds and accept integer values only. title: AutomaticLeaveConfig SetupCronRequestDefaultBotConfig: type: object properties: bot_name: type: string audio_required: type: boolean video_required: type: boolean bot_message: type: string bot_image_url: type: string automatic_leave: $ref: '#/components/schemas/AutomaticLeaveConfig' title: SetupCronRequestDefaultBotConfig SetupCronRequest: type: object properties: default_bot_config: $ref: '#/components/schemas/SetupCronRequestDefaultBotConfig' title: SetupCronRequest Calendar_disableCron_Response_200: type: object properties: {} description: Empty response body title: Calendar_disableCron_Response_200 securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Request** ```json {} ``` **Response** ```json {} ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/calendar/auto-schedule/disable" payload = {} headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/calendar/auto-schedule/disable'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/calendar/auto-schedule/disable" payload := strings.NewReader("{}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/calendar/auto-schedule/disable") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{}" response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/calendar/auto-schedule/disable") .header("Authorization", "") .header("Content-Type", "application/json") .body("{}") .asString(); ``` ```php request('POST', 'https://api.meetstream.ai/api/v1/calendar/auto-schedule/disable', [ 'body' => '{}', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/calendar/auto-schedule/disable"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/calendar/auto-schedule/disable")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Create Google Domain POST https://api.meetstream.ai/api/v1/google-login-domains Content-Type: application/json Reference: https://docs.meetstream.ai/api-reference/api-endpoints/google-signed-in-bots/create-google-domain ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/google-login-domains: post: operationId: create-google-domain summary: Create Google Domain tags: - subpackage_googleLogin parameters: - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/CreateGoogleDomainResponse' requestBody: content: application/json: schema: $ref: '#/components/schemas/LoginGroupRequest' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: LoginGroupRequestLoginMode: type: string enum: - always - if_required title: LoginGroupRequestLoginMode LoginGroupRequest: type: object properties: user_id: type: string name: type: string login_mode: $ref: '#/components/schemas/LoginGroupRequestLoginMode' required: - user_id - name title: LoginGroupRequest CreateGoogleDomainResponse: type: object properties: sso_workspace_domain: type: string name: type: string login_mode: type: string max_concurrent_per_login: type: integer created_at: type: string title: CreateGoogleDomainResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Request** ```json { "user_id": "string", "name": "string" } ``` **Response** ```json { "sso_workspace_domain": "maddy@group.com", "name": "Maddy-group", "login_mode": "always", "max_concurrent_per_login": 30, "created_at": "2026-03-20T22:56:27Z" } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/google-login-domains" payload = { "user_id": "string", "name": "string" } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/google-login-domains'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"user_id":"string","name":"string"}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/google-login-domains" payload := strings.NewReader("{\n \"user_id\": \"string\",\n \"name\": \"string\"\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/google-login-domains") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"user_id\": \"string\",\n \"name\": \"string\"\n}" response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/google-login-domains") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"user_id\": \"string\",\n \"name\": \"string\"\n}") .asString(); ``` ```php request('POST', 'https://api.meetstream.ai/api/v1/google-login-domains', [ 'body' => '{ "user_id": "string", "name": "string" }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/google-login-domains"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"user_id\": \"string\",\n \"name\": \"string\"\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "user_id": "string", "name": "string" ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/google-login-domains")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # List Google Domains GET https://api.meetstream.ai/api/v1/google-login-domains Reference: https://docs.meetstream.ai/api-reference/api-endpoints/google-signed-in-bots/list-google-domains ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/google-login-domains: get: operationId: list-google-domains summary: List Google Domains tags: - subpackage_googleLogin parameters: - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/ListGoogleDomainsResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: ListGoogleDomainsResponseDomainsItems: type: object properties: sso_workspace_domain: type: string name: type: string login_mode: type: string max_concurrent_per_login: type: string login_count: type: integer active_login_count: type: integer created_at: type: string title: ListGoogleDomainsResponseDomainsItems ListGoogleDomainsResponse: type: object properties: domains: type: array items: $ref: '#/components/schemas/ListGoogleDomainsResponseDomainsItems' title: ListGoogleDomainsResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "domains": [ { "sso_workspace_domain": "meetst", "name": "meetst", "login_mode": "always", "max_concurrent_per_login": "30", "login_count": 0, "active_login_count": 0, "created_at": "2026-03-14T19:43:43Z" } ] } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/google-login-domains" headers = {"Authorization": ""} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/google-login-domains'; const options = {method: 'GET', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/google-login-domains" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/google-login-domains") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.meetstream.ai/api/v1/google-login-domains") .header("Authorization", "") .asString(); ``` ```php request('GET', 'https://api.meetstream.ai/api/v1/google-login-domains', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/google-login-domains"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/google-login-domains")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Get Google Domain GET https://api.meetstream.ai/api/v1/google-login-domains/{domain} Reference: https://docs.meetstream.ai/api-reference/api-endpoints/google-signed-in-bots/get-google-domain ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/google-login-domains/{domain}: get: operationId: get-google-domain summary: Get Google Domain tags: - subpackage_googleLogin parameters: - name: domain in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/GetGoogleDomainResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: GetGoogleDomainResponseLoginsItems: type: object properties: login_id: type: string email: type: string is_active: type: boolean active_sessions: type: integer last_test_status: type: string last_tested_at: type: - string - 'null' title: GetGoogleDomainResponseLoginsItems GetGoogleDomainResponse: type: object properties: sso_workspace_domain: type: string name: type: string login_mode: type: string max_concurrent_per_login: type: string logins: type: array items: $ref: '#/components/schemas/GetGoogleDomainResponseLoginsItems' created_at: type: string updated_at: type: string title: GetGoogleDomainResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "sso_workspace_domain": "salesrobin.ai", "name": "Salesrogin_SSO", "login_mode": "always", "max_concurrent_per_login": "30", "logins": [ { "login_id": "f23d23c5-49bf-4237-a7cd-945a0a20aa02", "email": "agent@salesrobin.ai", "is_active": true, "active_sessions": 0, "last_test_status": "not_tested", "last_tested_at": "string" } ], "created_at": "2026-02-25T17:52:47Z", "updated_at": "2026-02-25T17:52:47Z" } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/google-login-domains/domain" headers = {"Authorization": ""} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/google-login-domains/domain'; const options = {method: 'GET', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/google-login-domains/domain" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/google-login-domains/domain") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.meetstream.ai/api/v1/google-login-domains/domain") .header("Authorization", "") .asString(); ``` ```php request('GET', 'https://api.meetstream.ai/api/v1/google-login-domains/domain', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/google-login-domains/domain"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/google-login-domains/domain")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Update Google Domain PATCH https://api.meetstream.ai/api/v1/google-login-domains/{domain} Reference: https://docs.meetstream.ai/api-reference/api-endpoints/google-signed-in-bots/update-google-domain ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/google-login-domains/{domain}: patch: operationId: update-google-domain summary: Update Google Domain tags: - subpackage_googleLogin parameters: - name: domain in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/UpdateGoogleDomainResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: UpdateGoogleDomainResponseDomain: type: object properties: sso_workspace_domain: type: string name: type: string login_mode: type: string updated_at: type: string title: UpdateGoogleDomainResponseDomain UpdateGoogleDomainResponse: type: object properties: success: type: boolean domain: $ref: '#/components/schemas/UpdateGoogleDomainResponseDomain' title: UpdateGoogleDomainResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "success": true, "domain": { "sso_workspace_domain": "salesrobin.ai", "name": "Maddy-groupp", "login_mode": "always", "updated_at": "2026-03-21T17:28:23Z" } } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/google-login-domains/domain" headers = {"Authorization": ""} response = requests.patch(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/google-login-domains/domain'; const options = {method: 'PATCH', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/google-login-domains/domain" req, _ := http.NewRequest("PATCH", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/google-login-domains/domain") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Patch.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.patch("https://api.meetstream.ai/api/v1/google-login-domains/domain") .header("Authorization", "") .asString(); ``` ```php request('PATCH', 'https://api.meetstream.ai/api/v1/google-login-domains/domain', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/google-login-domains/domain"); var request = new RestRequest(Method.PATCH); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/google-login-domains/domain")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "PATCH" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Delete Google Domain DELETE https://api.meetstream.ai/api/v1/google-login-domains/{domain} Reference: https://docs.meetstream.ai/api-reference/api-endpoints/google-signed-in-bots/delete-google-domain ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/google-login-domains/{domain}: delete: operationId: delete-google-domain summary: Delete Google Domain tags: - subpackage_googleLogin parameters: - name: domain in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: >- #/components/schemas/Google Login_deleteGoogleDomain_Response_200 servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: Google Login_deleteGoogleDomain_Response_200: type: object properties: {} description: Empty response body title: Google Login_deleteGoogleDomain_Response_200 securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json {} ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/google-login-domains/domain" headers = {"Authorization": ""} response = requests.delete(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/google-login-domains/domain'; const options = {method: 'DELETE', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/google-login-domains/domain" req, _ := http.NewRequest("DELETE", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/google-login-domains/domain") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Delete.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.delete("https://api.meetstream.ai/api/v1/google-login-domains/domain") .header("Authorization", "") .asString(); ``` ```php request('DELETE', 'https://api.meetstream.ai/api/v1/google-login-domains/domain', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/google-login-domains/domain"); var request = new RestRequest(Method.DELETE); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/google-login-domains/domain")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "DELETE" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Create Google Login POST https://api.meetstream.ai/api/v1/google-logins Reference: https://docs.meetstream.ai/api-reference/api-endpoints/google-signed-in-bots/create-google-login ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/google-logins: post: operationId: create-google-login summary: Create Google Login tags: - subpackage_googleLogin parameters: - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/CreateGoogleLoginResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: CreateGoogleLoginResponse: type: object properties: login_id: type: string domain: type: string email: type: string is_active: type: boolean created_at: type: string title: CreateGoogleLoginResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "login_id": "9e352133-029e-4ca5-8857-9445c67d7dbb", "domain": "salesrobin.ai", "email": "agenty@salesrobin.ai", "is_active": true, "created_at": "2026-03-21T17:38:21Z" } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/google-logins" headers = {"Authorization": ""} response = requests.post(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/google-logins'; const options = {method: 'POST', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/google-logins" req, _ := http.NewRequest("POST", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/google-logins") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/google-logins") .header("Authorization", "") .asString(); ``` ```php request('POST', 'https://api.meetstream.ai/api/v1/google-logins', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/google-logins"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/google-logins")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # List Google Logins GET https://api.meetstream.ai/api/v1/google-logins Reference: https://docs.meetstream.ai/api-reference/api-endpoints/google-signed-in-bots/list-google-logins ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/google-logins: get: operationId: list-google-logins summary: List Google Logins tags: - subpackage_googleLogin parameters: - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/ListGoogleLoginsResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: ListGoogleLoginsResponseLoginsItems: type: object properties: login_id: type: string email: type: string is_active: type: boolean active_sessions: type: integer last_test_status: type: string last_tested_at: type: - string - 'null' created_at: type: string title: ListGoogleLoginsResponseLoginsItems ListGoogleLoginsResponse: type: object properties: logins: type: array items: $ref: '#/components/schemas/ListGoogleLoginsResponseLoginsItems' title: ListGoogleLoginsResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "logins": [ { "login_id": "9e352133-029e-4ca5-8857-9445c67d7dbb", "email": "agenty@salesrobin.ai", "is_active": true, "active_sessions": 0, "last_test_status": "not_tested", "last_tested_at": "string", "created_at": "2026-03-21T17:38:21Z" } ] } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/google-logins" headers = {"Authorization": ""} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/google-logins'; const options = {method: 'GET', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/google-logins" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/google-logins") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.meetstream.ai/api/v1/google-logins") .header("Authorization", "") .asString(); ``` ```php request('GET', 'https://api.meetstream.ai/api/v1/google-logins', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/google-logins"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/google-logins")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Update Google Login PATCH https://api.meetstream.ai/api/v1/google-logins/{login_id} Reference: https://docs.meetstream.ai/api-reference/api-endpoints/google-signed-in-bots/update-google-login ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/google-logins/{login_id}: patch: operationId: update-google-login summary: Update Google Login tags: - subpackage_googleLogin parameters: - name: login_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/UpdateGoogleLoginResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: UpdateGoogleLoginResponseLogin: type: object properties: login_id: type: string email: type: string is_active: type: boolean updated_at: type: string title: UpdateGoogleLoginResponseLogin UpdateGoogleLoginResponse: type: object properties: success: type: boolean login: $ref: '#/components/schemas/UpdateGoogleLoginResponseLogin' title: UpdateGoogleLoginResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "success": true, "login": { "login_id": "9e352133-029e-4ca5-8857-9445c67d7dbb", "email": "agenty@salesrobin.ai", "is_active": false, "updated_at": "2026-03-21T17:45:48Z" } } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/google-logins/login_id" headers = {"Authorization": ""} response = requests.patch(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/google-logins/login_id'; const options = {method: 'PATCH', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/google-logins/login_id" req, _ := http.NewRequest("PATCH", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/google-logins/login_id") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Patch.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.patch("https://api.meetstream.ai/api/v1/google-logins/login_id") .header("Authorization", "") .asString(); ``` ```php request('PATCH', 'https://api.meetstream.ai/api/v1/google-logins/login_id', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/google-logins/login_id"); var request = new RestRequest(Method.PATCH); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/google-logins/login_id")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "PATCH" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Delete Google Login DELETE https://api.meetstream.ai/api/v1/google-logins/{login_id} Reference: https://docs.meetstream.ai/api-reference/api-endpoints/google-signed-in-bots/delete-google-login ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/google-logins/{login_id}: delete: operationId: delete-google-login summary: Delete Google Login tags: - subpackage_googleLogin parameters: - name: login_id in: path required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/DeleteGoogleLoginResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: DeleteGoogleLoginResponse: type: object properties: success: type: boolean message: type: string title: DeleteGoogleLoginResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "success": true, "message": "Login deleted successfully" } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/google-logins/login_id" headers = {"Authorization": ""} response = requests.delete(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/google-logins/login_id'; const options = {method: 'DELETE', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/google-logins/login_id" req, _ := http.NewRequest("DELETE", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/google-logins/login_id") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Delete.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.delete("https://api.meetstream.ai/api/v1/google-logins/login_id") .header("Authorization", "") .asString(); ``` ```php request('DELETE', 'https://api.meetstream.ai/api/v1/google-logins/login_id', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/google-logins/login_id"); var request = new RestRequest(Method.DELETE); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/google-logins/login_id")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "DELETE" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Create Agent Config POST https://api.meetstream.ai/api/v1/mia Content-Type: application/json For more information on customizing the payload, please refer to [MIA Configurations](https://docs.meetstream.ai/guides/mia-meetstream-infrastructure-agents/mia-configurations) in our guides. Reference: https://docs.meetstream.ai/api-reference/api-endpoints/mia/create-agent-config ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/mia: post: operationId: create-agent-config summary: Create Agent Config description: >- For more information on customizing the payload, please refer to [MIA Configurations](https://docs.meetstream.ai/guides/mia-meetstream-infrastructure-agents/mia-configurations) in our guides. tags: - subpackage_mia parameters: - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '201': description: Created content: application/json: schema: $ref: '#/components/schemas/MIACreateConfigResponse' requestBody: content: application/json: schema: $ref: '#/components/schemas/MIACreateConfigRequest' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: MiaCreateConfigRequestMode: type: string enum: - pipeline - realtime title: MiaCreateConfigRequestMode MiaModelConfigThinkingConfig: type: object properties: include_thoughts: type: boolean thinking_budget: type: integer title: MiaModelConfigThinkingConfig MIAModelConfig: type: object properties: provider: type: string model: type: string system_prompt: type: string first_message: type: string max_token: type: integer temperature: type: number format: double voice: type: string description: Used for realtime mode models that have a built-in voice. modalities: type: array items: type: string max_response_output_tokens: type: integer max_tokens: type: integer top_p: type: number format: double frequency_penalty: type: number format: double presence_penalty: type: number format: double thinking_config: $ref: '#/components/schemas/MiaModelConfigThinkingConfig' enable_affective_dialog: type: boolean proactivity: type: boolean disable_automatic_activity_detection: type: boolean title: MIAModelConfig MIAVoiceConfig: type: object properties: provider: type: string model: type: string voice_id: type: string speed: type: number format: double title: MIAVoiceConfig MIATranscriberConfig: type: object properties: provider: type: string model: type: string language: type: string boostwords: type: array items: type: string title: MIATranscriberConfig MiaAgentConfigInterruptionMode: type: string enum: - adaptive - vad title: MiaAgentConfigInterruptionMode MIAInterruptionsConfig: type: object properties: min_duration_seconds: type: number format: double word_threshold: type: integer title: MIAInterruptionsConfig MIAAgentConfig: type: object properties: tools: type: array items: type: string preemptive_generation: type: boolean user_away_timeout: type: number format: double interruption_mode: $ref: '#/components/schemas/MiaAgentConfigInterruptionMode' interruptions: $ref: '#/components/schemas/MIAInterruptionsConfig' false_interruption_timeout: type: number format: double vad_eagerness: type: string vad_type: type: string enable_interruptions: type: boolean resume_false_interruption: type: boolean response_modality: type: string mcp_servers: type: object additionalProperties: description: Any type tools_enabled: type: boolean vad_threshold: type: number format: double vad_prefix_padding_ms: type: integer vad_silence_duration_ms: type: integer turn_detection: type: string vad_activation_threshold: type: number format: double vad_deactivation_threshold: type: - number - 'null' format: double vad_min_silence_duration_ms: type: integer vad_min_speech_duration_ms: type: integer vad_prefix_padding_duration_ms: type: integer endpointing_mode: type: string min_endpointing_delay: type: number format: double max_endpointing_delay: type: number format: double title: MIAAgentConfig MIAAudioConfig: type: object properties: sample_rate: type: integer num_channels: type: integer title: MIAAudioConfig MIAWakeWordConfig: type: object properties: enabled: type: boolean words: type: array items: type: string timeout: type: integer title: MIAWakeWordConfig MIAAvatarConfig: type: object properties: provider: type: string enabled: type: boolean avatar_id: type: string title: MIAAvatarConfig MIACreateConfigRequest: type: object properties: agent_name: type: string mode: $ref: '#/components/schemas/MiaCreateConfigRequestMode' model: $ref: '#/components/schemas/MIAModelConfig' voice: $ref: '#/components/schemas/MIAVoiceConfig' transcriber: $ref: '#/components/schemas/MIATranscriberConfig' agent: $ref: '#/components/schemas/MIAAgentConfig' audio: $ref: '#/components/schemas/MIAAudioConfig' wake_word: oneOf: - $ref: '#/components/schemas/MIAWakeWordConfig' - type: 'null' Avatar: $ref: '#/components/schemas/MIAAvatarConfig' required: - agent_name - mode - model title: MIACreateConfigRequest MiaAgentConfigObjectMode: type: string enum: - pipeline - realtime title: MiaAgentConfigObjectMode MIAAgentConfigObject: type: object properties: AgentConfigID: type: string UserID: type: string AgentName: type: string Mode: $ref: '#/components/schemas/MiaAgentConfigObjectMode' Model: $ref: '#/components/schemas/MIAModelConfig' Voice: oneOf: - $ref: '#/components/schemas/MIAVoiceConfig' - type: 'null' Transcriber: oneOf: - $ref: '#/components/schemas/MIATranscriberConfig' - type: 'null' Agent: $ref: '#/components/schemas/MIAAgentConfig' Audio: $ref: '#/components/schemas/MIAAudioConfig' Avatar: $ref: '#/components/schemas/MIAAvatarConfig' WakeWord: type: object additionalProperties: description: Any type CreatedAt: type: string UpdatedAt: type: string description: A full agent configuration object as returned by the API. title: MIAAgentConfigObject MIACreateConfigResponse: type: object properties: message: type: string agent_config_id: type: string agent_config: $ref: '#/components/schemas/MIAAgentConfigObject' title: MIACreateConfigResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples ### Pipeline - OpenAI + elevenlabs + OpenAI **Request** ```json { "agent_name": "MeetStream's agent", "mode": "pipeline", "model": { "provider": "openai", "model": "gpt-4o-mini", "system_prompt": "You are a helpful meeting assistant. Keep responses concise and friendly. Speak naturally and conversationally.", "first_message": "Hi! I'm your AI meeting assistant. How can I help you today?", "max_token": 2048, "temperature": 0.8 }, "voice": { "provider": "elevenlabs", "model": "tts-1", "voice_id": "alloy" }, "transcriber": { "provider": "openai", "model": "whisper-1", "language": "en" }, "agent": { "tools": [ "current_time", "weather_now" ], "preemptive_generation": true, "user_away_timeout": 15 }, "audio": { "sample_rate": 24000, "num_channels": 1 } } ``` **Response** ```json { "message": "Agent configuration created successfully.", "agent_config_id": "d3ffa96f-85be-4037-81d0-a8eeb80c4876", "agent_config": { "AgentConfigID": "d3ffa96f-85be-4037-81d0-a8eeb80c4876", "UserID": "34e86428-0081-70e8-5bfb-3fa111d0ff77", "AgentName": "MeetStream's agent", "Mode": "pipeline", "Model": { "provider": "openai", "model": "gpt-4o-mini", "system_prompt": "You are a helpful meeting assistant. Keep responses concise and friendly. Speak naturally and conversationally.", "first_message": "Hi! I'm your AI meeting assistant. How can I help you today?", "max_token": 2048, "temperature": 0.8 }, "Voice": { "provider": "elevenlabs", "model": "tts-1", "voice_id": "alloy" }, "Transcriber": { "provider": "openai", "model": "whisper-1", "language": "en", "boostwords": [] }, "Agent": { "tools": [ "current_time", "weather_now" ], "preemptive_generation": true, "user_away_timeout": 15 }, "Audio": { "sample_rate": 24000, "num_channels": 1 }, "Avatar": null, "WakeWord": null, "CreatedAt": "2026-04-24T11:31:09Z", "UpdatedAt": "2026-04-24T11:31:09Z" } } ``` **SDK Code** ```python Pipeline - OpenAI + elevenlabs + OpenAI import requests url = "https://api.meetstream.ai/api/v1/mia" payload = { "agent_name": "MeetStream's agent", "mode": "pipeline", "model": { "provider": "openai", "model": "gpt-4o-mini", "system_prompt": "You are a helpful meeting assistant. Keep responses concise and friendly. Speak naturally and conversationally.", "first_message": "Hi! I'm your AI meeting assistant. How can I help you today?", "max_token": 2048, "temperature": 0.8 }, "voice": { "provider": "elevenlabs", "model": "tts-1", "voice_id": "alloy" }, "transcriber": { "provider": "openai", "model": "whisper-1", "language": "en" }, "agent": { "tools": ["current_time", "weather_now"], "preemptive_generation": True, "user_away_timeout": 15 }, "audio": { "sample_rate": 24000, "num_channels": 1 } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Pipeline - OpenAI + elevenlabs + OpenAI const url = 'https://api.meetstream.ai/api/v1/mia'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"agent_name":"MeetStream\'s agent","mode":"pipeline","model":{"provider":"openai","model":"gpt-4o-mini","system_prompt":"You are a helpful meeting assistant. Keep responses concise and friendly. Speak naturally and conversationally.","first_message":"Hi! I\'m your AI meeting assistant. How can I help you today?","max_token":2048,"temperature":0.8},"voice":{"provider":"elevenlabs","model":"tts-1","voice_id":"alloy"},"transcriber":{"provider":"openai","model":"whisper-1","language":"en"},"agent":{"tools":["current_time","weather_now"],"preemptive_generation":true,"user_away_timeout":15},"audio":{"sample_rate":24000,"num_channels":1}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Pipeline - OpenAI + elevenlabs + OpenAI package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/mia" payload := strings.NewReader("{\n \"agent_name\": \"MeetStream's agent\",\n \"mode\": \"pipeline\",\n \"model\": {\n \"provider\": \"openai\",\n \"model\": \"gpt-4o-mini\",\n \"system_prompt\": \"You are a helpful meeting assistant. Keep responses concise and friendly. Speak naturally and conversationally.\",\n \"first_message\": \"Hi! I'm your AI meeting assistant. How can I help you today?\",\n \"max_token\": 2048,\n \"temperature\": 0.8\n },\n \"voice\": {\n \"provider\": \"elevenlabs\",\n \"model\": \"tts-1\",\n \"voice_id\": \"alloy\"\n },\n \"transcriber\": {\n \"provider\": \"openai\",\n \"model\": \"whisper-1\",\n \"language\": \"en\"\n },\n \"agent\": {\n \"tools\": [\n \"current_time\",\n \"weather_now\"\n ],\n \"preemptive_generation\": true,\n \"user_away_timeout\": 15\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Pipeline - OpenAI + elevenlabs + OpenAI require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/mia") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"agent_name\": \"MeetStream's agent\",\n \"mode\": \"pipeline\",\n \"model\": {\n \"provider\": \"openai\",\n \"model\": \"gpt-4o-mini\",\n \"system_prompt\": \"You are a helpful meeting assistant. Keep responses concise and friendly. Speak naturally and conversationally.\",\n \"first_message\": \"Hi! I'm your AI meeting assistant. How can I help you today?\",\n \"max_token\": 2048,\n \"temperature\": 0.8\n },\n \"voice\": {\n \"provider\": \"elevenlabs\",\n \"model\": \"tts-1\",\n \"voice_id\": \"alloy\"\n },\n \"transcriber\": {\n \"provider\": \"openai\",\n \"model\": \"whisper-1\",\n \"language\": \"en\"\n },\n \"agent\": {\n \"tools\": [\n \"current_time\",\n \"weather_now\"\n ],\n \"preemptive_generation\": true,\n \"user_away_timeout\": 15\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n }\n}" response = http.request(request) puts response.read_body ``` ```java Pipeline - OpenAI + elevenlabs + OpenAI import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/mia") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"agent_name\": \"MeetStream's agent\",\n \"mode\": \"pipeline\",\n \"model\": {\n \"provider\": \"openai\",\n \"model\": \"gpt-4o-mini\",\n \"system_prompt\": \"You are a helpful meeting assistant. Keep responses concise and friendly. Speak naturally and conversationally.\",\n \"first_message\": \"Hi! I'm your AI meeting assistant. How can I help you today?\",\n \"max_token\": 2048,\n \"temperature\": 0.8\n },\n \"voice\": {\n \"provider\": \"elevenlabs\",\n \"model\": \"tts-1\",\n \"voice_id\": \"alloy\"\n },\n \"transcriber\": {\n \"provider\": \"openai\",\n \"model\": \"whisper-1\",\n \"language\": \"en\"\n },\n \"agent\": {\n \"tools\": [\n \"current_time\",\n \"weather_now\"\n ],\n \"preemptive_generation\": true,\n \"user_away_timeout\": 15\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n }\n}") .asString(); ``` ```php Pipeline - OpenAI + elevenlabs + OpenAI request('POST', 'https://api.meetstream.ai/api/v1/mia', [ 'body' => '{ "agent_name": "MeetStream\'s agent", "mode": "pipeline", "model": { "provider": "openai", "model": "gpt-4o-mini", "system_prompt": "You are a helpful meeting assistant. Keep responses concise and friendly. Speak naturally and conversationally.", "first_message": "Hi! I\'m your AI meeting assistant. How can I help you today?", "max_token": 2048, "temperature": 0.8 }, "voice": { "provider": "elevenlabs", "model": "tts-1", "voice_id": "alloy" }, "transcriber": { "provider": "openai", "model": "whisper-1", "language": "en" }, "agent": { "tools": [ "current_time", "weather_now" ], "preemptive_generation": true, "user_away_timeout": 15 }, "audio": { "sample_rate": 24000, "num_channels": 1 } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Pipeline - OpenAI + elevenlabs + OpenAI using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/mia"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"agent_name\": \"MeetStream's agent\",\n \"mode\": \"pipeline\",\n \"model\": {\n \"provider\": \"openai\",\n \"model\": \"gpt-4o-mini\",\n \"system_prompt\": \"You are a helpful meeting assistant. Keep responses concise and friendly. Speak naturally and conversationally.\",\n \"first_message\": \"Hi! I'm your AI meeting assistant. How can I help you today?\",\n \"max_token\": 2048,\n \"temperature\": 0.8\n },\n \"voice\": {\n \"provider\": \"elevenlabs\",\n \"model\": \"tts-1\",\n \"voice_id\": \"alloy\"\n },\n \"transcriber\": {\n \"provider\": \"openai\",\n \"model\": \"whisper-1\",\n \"language\": \"en\"\n },\n \"agent\": {\n \"tools\": [\n \"current_time\",\n \"weather_now\"\n ],\n \"preemptive_generation\": true,\n \"user_away_timeout\": 15\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Pipeline - OpenAI + elevenlabs + OpenAI import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "agent_name": "MeetStream's agent", "mode": "pipeline", "model": [ "provider": "openai", "model": "gpt-4o-mini", "system_prompt": "You are a helpful meeting assistant. Keep responses concise and friendly. Speak naturally and conversationally.", "first_message": "Hi! I'm your AI meeting assistant. How can I help you today?", "max_token": 2048, "temperature": 0.8 ], "voice": [ "provider": "elevenlabs", "model": "tts-1", "voice_id": "alloy" ], "transcriber": [ "provider": "openai", "model": "whisper-1", "language": "en" ], "agent": [ "tools": ["current_time", "weather_now"], "preemptive_generation": true, "user_away_timeout": 15 ], "audio": [ "sample_rate": 24000, "num_channels": 1 ] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/mia")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Realtime - OpenAI **Request** ```json { "agent_name": "New MIA Agent", "mode": "realtime", "model": { "provider": "openai", "model": "gpt-realtime-mini", "system_prompt": "You are a helpful AI meeting assistant. Keep responses concise and natural. Listen actively and provide value to the conversation.", "first_message": "Hey there! I am your AI assistant, ready to help with your meeting.", "temperature": 0.8, "voice": "coral", "modalities": [ "text", "audio" ], "max_response_output_tokens": 200 }, "voice": null, "transcriber": null, "agent": { "tools": [], "preemptive_generation": false, "user_away_timeout": 15, "interruptions": { "min_duration_seconds": 0.5, "word_threshold": 0 }, "false_interruption_timeout": 2, "vad_type": "server_vad", "enable_interruptions": true, "resume_false_interruption": true, "tools_enabled": false, "vad_threshold": 0.5, "vad_prefix_padding_ms": 0, "vad_silence_duration_ms": 200 }, "audio": { "sample_rate": 24000, "num_channels": 1 }, "wake_word": null } ``` **Response** ```json { "message": "Agent configuration created successfully.", "agent_config_id": "f64aeb25-0b8c-478f-ae4a-f0f6438be32d", "agent_config": { "AgentConfigID": "f64aeb25-0b8c-478f-ae4a-f0f6438be32d", "UserID": "34e86428-0081-70e8-5bfb-3fa111d0ff77", "AgentName": "New MIA Agent", "Mode": "realtime", "Model": { "provider": "openai", "model": "gpt-realtime-mini", "system_prompt": "You are a helpful AI meeting assistant. Keep responses concise and natural. Listen actively and provide value to the conversation.", "first_message": "Hey there! I am your AI assistant, ready to help with your meeting.", "temperature": 0.8, "voice": "coral", "modalities": [ "text", "audio" ], "max_response_output_tokens": 200 }, "Voice": null, "Transcriber": null, "Agent": { "tools": [], "preemptive_generation": false, "user_away_timeout": 15, "interruptions": { "min_duration_seconds": 0.5, "word_threshold": 0 }, "false_interruption_timeout": 2, "vad_type": "server_vad", "enable_interruptions": true, "resume_false_interruption": true, "tools_enabled": false, "vad_threshold": 0.5, "vad_prefix_padding_ms": 0, "vad_silence_duration_ms": 200 }, "Audio": { "sample_rate": 24000, "num_channels": 1 }, "Avatar": null, "WakeWord": null, "CreatedAt": "2026-04-24T12:00:00Z", "UpdatedAt": "2026-04-24T12:00:00Z" } } ``` **SDK Code** ```python Realtime - OpenAI import requests url = "https://api.meetstream.ai/api/v1/mia" payload = { "agent_name": "New MIA Agent", "mode": "realtime", "model": { "provider": "openai", "model": "gpt-realtime-mini", "system_prompt": "You are a helpful AI meeting assistant. Keep responses concise and natural. Listen actively and provide value to the conversation.", "first_message": "Hey there! I am your AI assistant, ready to help with your meeting.", "temperature": 0.8, "voice": "coral", "modalities": ["text", "audio"], "max_response_output_tokens": 200 }, "voice": None, "transcriber": None, "agent": { "tools": [], "preemptive_generation": False, "user_away_timeout": 15, "interruptions": { "min_duration_seconds": 0.5, "word_threshold": 0 }, "false_interruption_timeout": 2, "vad_type": "server_vad", "enable_interruptions": True, "resume_false_interruption": True, "tools_enabled": False, "vad_threshold": 0.5, "vad_prefix_padding_ms": 0, "vad_silence_duration_ms": 200 }, "audio": { "sample_rate": 24000, "num_channels": 1 }, "wake_word": None } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Realtime - OpenAI const url = 'https://api.meetstream.ai/api/v1/mia'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"agent_name":"New MIA Agent","mode":"realtime","model":{"provider":"openai","model":"gpt-realtime-mini","system_prompt":"You are a helpful AI meeting assistant. Keep responses concise and natural. Listen actively and provide value to the conversation.","first_message":"Hey there! I am your AI assistant, ready to help with your meeting.","temperature":0.8,"voice":"coral","modalities":["text","audio"],"max_response_output_tokens":200},"voice":null,"transcriber":null,"agent":{"tools":[],"preemptive_generation":false,"user_away_timeout":15,"interruptions":{"min_duration_seconds":0.5,"word_threshold":0},"false_interruption_timeout":2,"vad_type":"server_vad","enable_interruptions":true,"resume_false_interruption":true,"tools_enabled":false,"vad_threshold":0.5,"vad_prefix_padding_ms":0,"vad_silence_duration_ms":200},"audio":{"sample_rate":24000,"num_channels":1},"wake_word":null}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Realtime - OpenAI package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/mia" payload := strings.NewReader("{\n \"agent_name\": \"New MIA Agent\",\n \"mode\": \"realtime\",\n \"model\": {\n \"provider\": \"openai\",\n \"model\": \"gpt-realtime-mini\",\n \"system_prompt\": \"You are a helpful AI meeting assistant. Keep responses concise and natural. Listen actively and provide value to the conversation.\",\n \"first_message\": \"Hey there! I am your AI assistant, ready to help with your meeting.\",\n \"temperature\": 0.8,\n \"voice\": \"coral\",\n \"modalities\": [\n \"text\",\n \"audio\"\n ],\n \"max_response_output_tokens\": 200\n },\n \"voice\": null,\n \"transcriber\": null,\n \"agent\": {\n \"tools\": [],\n \"preemptive_generation\": false,\n \"user_away_timeout\": 15,\n \"interruptions\": {\n \"min_duration_seconds\": 0.5,\n \"word_threshold\": 0\n },\n \"false_interruption_timeout\": 2,\n \"vad_type\": \"server_vad\",\n \"enable_interruptions\": true,\n \"resume_false_interruption\": true,\n \"tools_enabled\": false,\n \"vad_threshold\": 0.5,\n \"vad_prefix_padding_ms\": 0,\n \"vad_silence_duration_ms\": 200\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n },\n \"wake_word\": null\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Realtime - OpenAI require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/mia") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"agent_name\": \"New MIA Agent\",\n \"mode\": \"realtime\",\n \"model\": {\n \"provider\": \"openai\",\n \"model\": \"gpt-realtime-mini\",\n \"system_prompt\": \"You are a helpful AI meeting assistant. Keep responses concise and natural. Listen actively and provide value to the conversation.\",\n \"first_message\": \"Hey there! I am your AI assistant, ready to help with your meeting.\",\n \"temperature\": 0.8,\n \"voice\": \"coral\",\n \"modalities\": [\n \"text\",\n \"audio\"\n ],\n \"max_response_output_tokens\": 200\n },\n \"voice\": null,\n \"transcriber\": null,\n \"agent\": {\n \"tools\": [],\n \"preemptive_generation\": false,\n \"user_away_timeout\": 15,\n \"interruptions\": {\n \"min_duration_seconds\": 0.5,\n \"word_threshold\": 0\n },\n \"false_interruption_timeout\": 2,\n \"vad_type\": \"server_vad\",\n \"enable_interruptions\": true,\n \"resume_false_interruption\": true,\n \"tools_enabled\": false,\n \"vad_threshold\": 0.5,\n \"vad_prefix_padding_ms\": 0,\n \"vad_silence_duration_ms\": 200\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n },\n \"wake_word\": null\n}" response = http.request(request) puts response.read_body ``` ```java Realtime - OpenAI import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/mia") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"agent_name\": \"New MIA Agent\",\n \"mode\": \"realtime\",\n \"model\": {\n \"provider\": \"openai\",\n \"model\": \"gpt-realtime-mini\",\n \"system_prompt\": \"You are a helpful AI meeting assistant. Keep responses concise and natural. Listen actively and provide value to the conversation.\",\n \"first_message\": \"Hey there! I am your AI assistant, ready to help with your meeting.\",\n \"temperature\": 0.8,\n \"voice\": \"coral\",\n \"modalities\": [\n \"text\",\n \"audio\"\n ],\n \"max_response_output_tokens\": 200\n },\n \"voice\": null,\n \"transcriber\": null,\n \"agent\": {\n \"tools\": [],\n \"preemptive_generation\": false,\n \"user_away_timeout\": 15,\n \"interruptions\": {\n \"min_duration_seconds\": 0.5,\n \"word_threshold\": 0\n },\n \"false_interruption_timeout\": 2,\n \"vad_type\": \"server_vad\",\n \"enable_interruptions\": true,\n \"resume_false_interruption\": true,\n \"tools_enabled\": false,\n \"vad_threshold\": 0.5,\n \"vad_prefix_padding_ms\": 0,\n \"vad_silence_duration_ms\": 200\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n },\n \"wake_word\": null\n}") .asString(); ``` ```php Realtime - OpenAI request('POST', 'https://api.meetstream.ai/api/v1/mia', [ 'body' => '{ "agent_name": "New MIA Agent", "mode": "realtime", "model": { "provider": "openai", "model": "gpt-realtime-mini", "system_prompt": "You are a helpful AI meeting assistant. Keep responses concise and natural. Listen actively and provide value to the conversation.", "first_message": "Hey there! I am your AI assistant, ready to help with your meeting.", "temperature": 0.8, "voice": "coral", "modalities": [ "text", "audio" ], "max_response_output_tokens": 200 }, "voice": null, "transcriber": null, "agent": { "tools": [], "preemptive_generation": false, "user_away_timeout": 15, "interruptions": { "min_duration_seconds": 0.5, "word_threshold": 0 }, "false_interruption_timeout": 2, "vad_type": "server_vad", "enable_interruptions": true, "resume_false_interruption": true, "tools_enabled": false, "vad_threshold": 0.5, "vad_prefix_padding_ms": 0, "vad_silence_duration_ms": 200 }, "audio": { "sample_rate": 24000, "num_channels": 1 }, "wake_word": null }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Realtime - OpenAI using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/mia"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"agent_name\": \"New MIA Agent\",\n \"mode\": \"realtime\",\n \"model\": {\n \"provider\": \"openai\",\n \"model\": \"gpt-realtime-mini\",\n \"system_prompt\": \"You are a helpful AI meeting assistant. Keep responses concise and natural. Listen actively and provide value to the conversation.\",\n \"first_message\": \"Hey there! I am your AI assistant, ready to help with your meeting.\",\n \"temperature\": 0.8,\n \"voice\": \"coral\",\n \"modalities\": [\n \"text\",\n \"audio\"\n ],\n \"max_response_output_tokens\": 200\n },\n \"voice\": null,\n \"transcriber\": null,\n \"agent\": {\n \"tools\": [],\n \"preemptive_generation\": false,\n \"user_away_timeout\": 15,\n \"interruptions\": {\n \"min_duration_seconds\": 0.5,\n \"word_threshold\": 0\n },\n \"false_interruption_timeout\": 2,\n \"vad_type\": \"server_vad\",\n \"enable_interruptions\": true,\n \"resume_false_interruption\": true,\n \"tools_enabled\": false,\n \"vad_threshold\": 0.5,\n \"vad_prefix_padding_ms\": 0,\n \"vad_silence_duration_ms\": 200\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n },\n \"wake_word\": null\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Realtime - OpenAI import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "agent_name": "New MIA Agent", "mode": "realtime", "model": [ "provider": "openai", "model": "gpt-realtime-mini", "system_prompt": "You are a helpful AI meeting assistant. Keep responses concise and natural. Listen actively and provide value to the conversation.", "first_message": "Hey there! I am your AI assistant, ready to help with your meeting.", "temperature": 0.8, "voice": "coral", "modalities": ["text", "audio"], "max_response_output_tokens": 200 ], "voice": , "transcriber": , "agent": [ "tools": [], "preemptive_generation": false, "user_away_timeout": 15, "interruptions": [ "min_duration_seconds": 0.5, "word_threshold": 0 ], "false_interruption_timeout": 2, "vad_type": "server_vad", "enable_interruptions": true, "resume_false_interruption": true, "tools_enabled": false, "vad_threshold": 0.5, "vad_prefix_padding_ms": 0, "vad_silence_duration_ms": 200 ], "audio": [ "sample_rate": 24000, "num_channels": 1 ], "wake_word": ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/mia")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Realtime - Gemini **Request** ```json { "agent_name": "Gemini Full MIA", "mode": "realtime", "model": { "provider": "google", "model": "gemini-2.5-flash-native-audio-preview-12-2025", "system_prompt": "You are an expressive, helpful AI meeting assistant with advanced capabilities. Be natural, engaging, and provide value to the conversation.", "temperature": 0.8, "voice": "Kore", "thinking_config": { "include_thoughts": false, "thinking_budget": 1024 }, "enable_affective_dialog": true, "proactivity": true, "disable_automatic_activity_detection": true }, "voice": null, "transcriber": null, "agent": { "preemptive_generation": false, "user_away_timeout": 15 }, "audio": { "sample_rate": 24000, "num_channels": 1 }, "wake_word": null } ``` **Response** ```json { "message": "Agent configuration created successfully.", "agent_config_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "agent_config": { "AgentConfigID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "UserID": "34e86428-0081-70e8-5bfb-3fa111d0ff77", "AgentName": "Gemini Full MIA", "Mode": "realtime", "Model": { "provider": "google", "model": "gemini-2.5-flash-native-audio-preview-12-2025", "system_prompt": "You are an expressive, helpful AI meeting assistant with advanced capabilities. Be natural, engaging, and provide value to the conversation.", "temperature": 0.8, "voice": "Kore", "thinking_config": { "include_thoughts": false, "thinking_budget": 1024 }, "enable_affective_dialog": true, "proactivity": true, "disable_automatic_activity_detection": true }, "Voice": null, "Transcriber": null, "Agent": { "preemptive_generation": false, "user_away_timeout": 15 }, "Audio": { "sample_rate": 24000, "num_channels": 1 }, "Avatar": null, "WakeWord": null, "CreatedAt": "2026-04-24T12:10:00Z", "UpdatedAt": "2026-04-24T12:10:00Z" } } ``` **SDK Code** ```python Realtime - Gemini import requests url = "https://api.meetstream.ai/api/v1/mia" payload = { "agent_name": "Gemini Full MIA", "mode": "realtime", "model": { "provider": "google", "model": "gemini-2.5-flash-native-audio-preview-12-2025", "system_prompt": "You are an expressive, helpful AI meeting assistant with advanced capabilities. Be natural, engaging, and provide value to the conversation.", "temperature": 0.8, "voice": "Kore", "thinking_config": { "include_thoughts": False, "thinking_budget": 1024 }, "enable_affective_dialog": True, "proactivity": True, "disable_automatic_activity_detection": True }, "voice": None, "transcriber": None, "agent": { "preemptive_generation": False, "user_away_timeout": 15 }, "audio": { "sample_rate": 24000, "num_channels": 1 }, "wake_word": None } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Realtime - Gemini const url = 'https://api.meetstream.ai/api/v1/mia'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"agent_name":"Gemini Full MIA","mode":"realtime","model":{"provider":"google","model":"gemini-2.5-flash-native-audio-preview-12-2025","system_prompt":"You are an expressive, helpful AI meeting assistant with advanced capabilities. Be natural, engaging, and provide value to the conversation.","temperature":0.8,"voice":"Kore","thinking_config":{"include_thoughts":false,"thinking_budget":1024},"enable_affective_dialog":true,"proactivity":true,"disable_automatic_activity_detection":true},"voice":null,"transcriber":null,"agent":{"preemptive_generation":false,"user_away_timeout":15},"audio":{"sample_rate":24000,"num_channels":1},"wake_word":null}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Realtime - Gemini package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/mia" payload := strings.NewReader("{\n \"agent_name\": \"Gemini Full MIA\",\n \"mode\": \"realtime\",\n \"model\": {\n \"provider\": \"google\",\n \"model\": \"gemini-2.5-flash-native-audio-preview-12-2025\",\n \"system_prompt\": \"You are an expressive, helpful AI meeting assistant with advanced capabilities. Be natural, engaging, and provide value to the conversation.\",\n \"temperature\": 0.8,\n \"voice\": \"Kore\",\n \"thinking_config\": {\n \"include_thoughts\": false,\n \"thinking_budget\": 1024\n },\n \"enable_affective_dialog\": true,\n \"proactivity\": true,\n \"disable_automatic_activity_detection\": true\n },\n \"voice\": null,\n \"transcriber\": null,\n \"agent\": {\n \"preemptive_generation\": false,\n \"user_away_timeout\": 15\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n },\n \"wake_word\": null\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Realtime - Gemini require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/mia") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"agent_name\": \"Gemini Full MIA\",\n \"mode\": \"realtime\",\n \"model\": {\n \"provider\": \"google\",\n \"model\": \"gemini-2.5-flash-native-audio-preview-12-2025\",\n \"system_prompt\": \"You are an expressive, helpful AI meeting assistant with advanced capabilities. Be natural, engaging, and provide value to the conversation.\",\n \"temperature\": 0.8,\n \"voice\": \"Kore\",\n \"thinking_config\": {\n \"include_thoughts\": false,\n \"thinking_budget\": 1024\n },\n \"enable_affective_dialog\": true,\n \"proactivity\": true,\n \"disable_automatic_activity_detection\": true\n },\n \"voice\": null,\n \"transcriber\": null,\n \"agent\": {\n \"preemptive_generation\": false,\n \"user_away_timeout\": 15\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n },\n \"wake_word\": null\n}" response = http.request(request) puts response.read_body ``` ```java Realtime - Gemini import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/mia") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"agent_name\": \"Gemini Full MIA\",\n \"mode\": \"realtime\",\n \"model\": {\n \"provider\": \"google\",\n \"model\": \"gemini-2.5-flash-native-audio-preview-12-2025\",\n \"system_prompt\": \"You are an expressive, helpful AI meeting assistant with advanced capabilities. Be natural, engaging, and provide value to the conversation.\",\n \"temperature\": 0.8,\n \"voice\": \"Kore\",\n \"thinking_config\": {\n \"include_thoughts\": false,\n \"thinking_budget\": 1024\n },\n \"enable_affective_dialog\": true,\n \"proactivity\": true,\n \"disable_automatic_activity_detection\": true\n },\n \"voice\": null,\n \"transcriber\": null,\n \"agent\": {\n \"preemptive_generation\": false,\n \"user_away_timeout\": 15\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n },\n \"wake_word\": null\n}") .asString(); ``` ```php Realtime - Gemini request('POST', 'https://api.meetstream.ai/api/v1/mia', [ 'body' => '{ "agent_name": "Gemini Full MIA", "mode": "realtime", "model": { "provider": "google", "model": "gemini-2.5-flash-native-audio-preview-12-2025", "system_prompt": "You are an expressive, helpful AI meeting assistant with advanced capabilities. Be natural, engaging, and provide value to the conversation.", "temperature": 0.8, "voice": "Kore", "thinking_config": { "include_thoughts": false, "thinking_budget": 1024 }, "enable_affective_dialog": true, "proactivity": true, "disable_automatic_activity_detection": true }, "voice": null, "transcriber": null, "agent": { "preemptive_generation": false, "user_away_timeout": 15 }, "audio": { "sample_rate": 24000, "num_channels": 1 }, "wake_word": null }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Realtime - Gemini using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/mia"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"agent_name\": \"Gemini Full MIA\",\n \"mode\": \"realtime\",\n \"model\": {\n \"provider\": \"google\",\n \"model\": \"gemini-2.5-flash-native-audio-preview-12-2025\",\n \"system_prompt\": \"You are an expressive, helpful AI meeting assistant with advanced capabilities. Be natural, engaging, and provide value to the conversation.\",\n \"temperature\": 0.8,\n \"voice\": \"Kore\",\n \"thinking_config\": {\n \"include_thoughts\": false,\n \"thinking_budget\": 1024\n },\n \"enable_affective_dialog\": true,\n \"proactivity\": true,\n \"disable_automatic_activity_detection\": true\n },\n \"voice\": null,\n \"transcriber\": null,\n \"agent\": {\n \"preemptive_generation\": false,\n \"user_away_timeout\": 15\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n },\n \"wake_word\": null\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Realtime - Gemini import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "agent_name": "Gemini Full MIA", "mode": "realtime", "model": [ "provider": "google", "model": "gemini-2.5-flash-native-audio-preview-12-2025", "system_prompt": "You are an expressive, helpful AI meeting assistant with advanced capabilities. Be natural, engaging, and provide value to the conversation.", "temperature": 0.8, "voice": "Kore", "thinking_config": [ "include_thoughts": false, "thinking_budget": 1024 ], "enable_affective_dialog": true, "proactivity": true, "disable_automatic_activity_detection": true ], "voice": , "transcriber": , "agent": [ "preemptive_generation": false, "user_away_timeout": 15 ], "audio": [ "sample_rate": 24000, "num_channels": 1 ], "wake_word": ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/mia")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Realtime - xAI Grok **Request** ```json { "agent_name": "MIA xAI Agent", "mode": "realtime", "model": { "provider": "xai", "system_prompt": "You are a helpful AI meeting assistant. Keep responses concise and natural.", "first_message": "Hey there! I am your AI assistant, ready to help with your meeting.", "voice": "Ara" }, "agent": { "tools": [], "preemptive_generation": false, "user_away_timeout": 15, "interruptions": { "min_duration_seconds": 0.5, "word_threshold": 0 }, "false_interruption_timeout": 2, "enable_interruptions": true, "resume_false_interruption": true }, "audio": { "sample_rate": 24000, "num_channels": 1 } } ``` **Response** ```json { "message": "Agent configuration created successfully.", "agent_config_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "agent_config": { "AgentConfigID": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "UserID": "34e86428-0081-70e8-5bfb-3fa111d0ff77", "AgentName": "MIA xAI Agent", "Mode": "realtime", "Model": { "provider": "xai", "system_prompt": "You are a helpful AI meeting assistant. Keep responses concise and natural.", "first_message": "Hey there! I am your AI assistant, ready to help with your meeting.", "voice": "Ara" }, "Voice": null, "Transcriber": null, "Agent": { "tools": [], "preemptive_generation": false, "user_away_timeout": 15, "interruptions": { "min_duration_seconds": 0.5, "word_threshold": 0 }, "false_interruption_timeout": 2, "enable_interruptions": true, "resume_false_interruption": true }, "Audio": { "sample_rate": 24000, "num_channels": 1 }, "Avatar": null, "WakeWord": null, "CreatedAt": "2026-04-24T12:20:00Z", "UpdatedAt": "2026-04-24T12:20:00Z" } } ``` **SDK Code** ```python Realtime - xAI Grok import requests url = "https://api.meetstream.ai/api/v1/mia" payload = { "agent_name": "MIA xAI Agent", "mode": "realtime", "model": { "provider": "xai", "system_prompt": "You are a helpful AI meeting assistant. Keep responses concise and natural.", "first_message": "Hey there! I am your AI assistant, ready to help with your meeting.", "voice": "Ara" }, "agent": { "tools": [], "preemptive_generation": False, "user_away_timeout": 15, "interruptions": { "min_duration_seconds": 0.5, "word_threshold": 0 }, "false_interruption_timeout": 2, "enable_interruptions": True, "resume_false_interruption": True }, "audio": { "sample_rate": 24000, "num_channels": 1 } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Realtime - xAI Grok const url = 'https://api.meetstream.ai/api/v1/mia'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"agent_name":"MIA xAI Agent","mode":"realtime","model":{"provider":"xai","system_prompt":"You are a helpful AI meeting assistant. Keep responses concise and natural.","first_message":"Hey there! I am your AI assistant, ready to help with your meeting.","voice":"Ara"},"agent":{"tools":[],"preemptive_generation":false,"user_away_timeout":15,"interruptions":{"min_duration_seconds":0.5,"word_threshold":0},"false_interruption_timeout":2,"enable_interruptions":true,"resume_false_interruption":true},"audio":{"sample_rate":24000,"num_channels":1}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Realtime - xAI Grok package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/mia" payload := strings.NewReader("{\n \"agent_name\": \"MIA xAI Agent\",\n \"mode\": \"realtime\",\n \"model\": {\n \"provider\": \"xai\",\n \"system_prompt\": \"You are a helpful AI meeting assistant. Keep responses concise and natural.\",\n \"first_message\": \"Hey there! I am your AI assistant, ready to help with your meeting.\",\n \"voice\": \"Ara\"\n },\n \"agent\": {\n \"tools\": [],\n \"preemptive_generation\": false,\n \"user_away_timeout\": 15,\n \"interruptions\": {\n \"min_duration_seconds\": 0.5,\n \"word_threshold\": 0\n },\n \"false_interruption_timeout\": 2,\n \"enable_interruptions\": true,\n \"resume_false_interruption\": true\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Realtime - xAI Grok require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/mia") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"agent_name\": \"MIA xAI Agent\",\n \"mode\": \"realtime\",\n \"model\": {\n \"provider\": \"xai\",\n \"system_prompt\": \"You are a helpful AI meeting assistant. Keep responses concise and natural.\",\n \"first_message\": \"Hey there! I am your AI assistant, ready to help with your meeting.\",\n \"voice\": \"Ara\"\n },\n \"agent\": {\n \"tools\": [],\n \"preemptive_generation\": false,\n \"user_away_timeout\": 15,\n \"interruptions\": {\n \"min_duration_seconds\": 0.5,\n \"word_threshold\": 0\n },\n \"false_interruption_timeout\": 2,\n \"enable_interruptions\": true,\n \"resume_false_interruption\": true\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n }\n}" response = http.request(request) puts response.read_body ``` ```java Realtime - xAI Grok import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/mia") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"agent_name\": \"MIA xAI Agent\",\n \"mode\": \"realtime\",\n \"model\": {\n \"provider\": \"xai\",\n \"system_prompt\": \"You are a helpful AI meeting assistant. Keep responses concise and natural.\",\n \"first_message\": \"Hey there! I am your AI assistant, ready to help with your meeting.\",\n \"voice\": \"Ara\"\n },\n \"agent\": {\n \"tools\": [],\n \"preemptive_generation\": false,\n \"user_away_timeout\": 15,\n \"interruptions\": {\n \"min_duration_seconds\": 0.5,\n \"word_threshold\": 0\n },\n \"false_interruption_timeout\": 2,\n \"enable_interruptions\": true,\n \"resume_false_interruption\": true\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n }\n}") .asString(); ``` ```php Realtime - xAI Grok request('POST', 'https://api.meetstream.ai/api/v1/mia', [ 'body' => '{ "agent_name": "MIA xAI Agent", "mode": "realtime", "model": { "provider": "xai", "system_prompt": "You are a helpful AI meeting assistant. Keep responses concise and natural.", "first_message": "Hey there! I am your AI assistant, ready to help with your meeting.", "voice": "Ara" }, "agent": { "tools": [], "preemptive_generation": false, "user_away_timeout": 15, "interruptions": { "min_duration_seconds": 0.5, "word_threshold": 0 }, "false_interruption_timeout": 2, "enable_interruptions": true, "resume_false_interruption": true }, "audio": { "sample_rate": 24000, "num_channels": 1 } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Realtime - xAI Grok using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/mia"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"agent_name\": \"MIA xAI Agent\",\n \"mode\": \"realtime\",\n \"model\": {\n \"provider\": \"xai\",\n \"system_prompt\": \"You are a helpful AI meeting assistant. Keep responses concise and natural.\",\n \"first_message\": \"Hey there! I am your AI assistant, ready to help with your meeting.\",\n \"voice\": \"Ara\"\n },\n \"agent\": {\n \"tools\": [],\n \"preemptive_generation\": false,\n \"user_away_timeout\": 15,\n \"interruptions\": {\n \"min_duration_seconds\": 0.5,\n \"word_threshold\": 0\n },\n \"false_interruption_timeout\": 2,\n \"enable_interruptions\": true,\n \"resume_false_interruption\": true\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Realtime - xAI Grok import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "agent_name": "MIA xAI Agent", "mode": "realtime", "model": [ "provider": "xai", "system_prompt": "You are a helpful AI meeting assistant. Keep responses concise and natural.", "first_message": "Hey there! I am your AI assistant, ready to help with your meeting.", "voice": "Ara" ], "agent": [ "tools": [], "preemptive_generation": false, "user_away_timeout": 15, "interruptions": [ "min_duration_seconds": 0.5, "word_threshold": 0 ], "false_interruption_timeout": 2, "enable_interruptions": true, "resume_false_interruption": true ], "audio": [ "sample_rate": 24000, "num_channels": 1 ] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/mia")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Pipeline - OpenAI + OpenAI + Deepgram **Request** ```json { "agent_name": "V's Pipeline Agent", "mode": "pipeline", "model": { "provider": "openai", "model": "gpt-5-nano", "system_prompt": "Hi", "first_message": "Hi", "temperature": 0.8, "max_tokens": 150, "top_p": 1, "frequency_penalty": 0, "presence_penalty": 0 }, "voice": { "provider": "openai", "model": "tts-1", "voice_id": "alloy", "speed": 1 }, "transcriber": { "provider": "deepgram", "model": "nova-2", "language": "en", "boostwords": [ "hey assistant", "MeetStream", "LiveKit" ] }, "agent": { "tools": [], "preemptive_generation": false, "user_away_timeout": 15, "interruption_mode": "adaptive", "interruptions": { "min_duration_seconds": 0.5, "word_threshold": 2 }, "false_interruption_timeout": 2, "enable_interruptions": true, "resume_false_interruption": true, "turn_detection": "vad", "vad_activation_threshold": 0.5, "vad_deactivation_threshold": null, "vad_min_silence_duration_ms": 550, "vad_min_speech_duration_ms": 50, "vad_prefix_padding_duration_ms": 300, "endpointing_mode": "fixed", "min_endpointing_delay": 0.5, "max_endpointing_delay": 3 }, "audio": { "sample_rate": 24000, "num_channels": 1 }, "wake_word": { "enabled": true, "words": [ "hello" ], "timeout": 30 } } ``` **Response** ```json { "message": "Agent configuration created successfully.", "agent_config_id": "c3d4e5f6-a7b8-9012-cdef-123456789012", "agent_config": { "AgentConfigID": "c3d4e5f6-a7b8-9012-cdef-123456789012", "UserID": "34e86428-0081-70e8-5bfb-3fa111d0ff77", "AgentName": "V's Pipeline Agent", "Mode": "pipeline", "Model": { "provider": "openai", "model": "gpt-5-nano", "system_prompt": "Hi", "first_message": "Hi", "temperature": 0.8, "max_tokens": 150, "top_p": 1, "frequency_penalty": 0, "presence_penalty": 0 }, "Voice": { "provider": "openai", "model": "tts-1", "voice_id": "alloy", "speed": 1 }, "Transcriber": { "provider": "deepgram", "model": "nova-2", "language": "en", "boostwords": [ "hey assistant", "MeetStream", "LiveKit" ] }, "Agent": { "tools": [], "preemptive_generation": false, "user_away_timeout": 15, "interruption_mode": "adaptive", "interruptions": { "min_duration_seconds": 0.5, "word_threshold": 2 }, "false_interruption_timeout": 2, "enable_interruptions": true, "resume_false_interruption": true, "turn_detection": "vad", "vad_activation_threshold": 0.5, "vad_deactivation_threshold": null, "vad_min_silence_duration_ms": 550, "vad_min_speech_duration_ms": 50, "vad_prefix_padding_duration_ms": 300, "endpointing_mode": "fixed", "min_endpointing_delay": 0.5, "max_endpointing_delay": 3 }, "Audio": { "sample_rate": 24000, "num_channels": 1 }, "Avatar": null, "WakeWord": { "enabled": true, "words": [ "hello" ], "timeout": 30 }, "CreatedAt": "2026-04-24T12:30:00Z", "UpdatedAt": "2026-04-24T12:30:00Z" } } ``` **SDK Code** ```python Pipeline - OpenAI + OpenAI + Deepgram import requests url = "https://api.meetstream.ai/api/v1/mia" payload = { "agent_name": "V's Pipeline Agent", "mode": "pipeline", "model": { "provider": "openai", "model": "gpt-5-nano", "system_prompt": "Hi", "first_message": "Hi", "temperature": 0.8, "max_tokens": 150, "top_p": 1, "frequency_penalty": 0, "presence_penalty": 0 }, "voice": { "provider": "openai", "model": "tts-1", "voice_id": "alloy", "speed": 1 }, "transcriber": { "provider": "deepgram", "model": "nova-2", "language": "en", "boostwords": ["hey assistant", "MeetStream", "LiveKit"] }, "agent": { "tools": [], "preemptive_generation": False, "user_away_timeout": 15, "interruption_mode": "adaptive", "interruptions": { "min_duration_seconds": 0.5, "word_threshold": 2 }, "false_interruption_timeout": 2, "enable_interruptions": True, "resume_false_interruption": True, "turn_detection": "vad", "vad_activation_threshold": 0.5, "vad_deactivation_threshold": None, "vad_min_silence_duration_ms": 550, "vad_min_speech_duration_ms": 50, "vad_prefix_padding_duration_ms": 300, "endpointing_mode": "fixed", "min_endpointing_delay": 0.5, "max_endpointing_delay": 3 }, "audio": { "sample_rate": 24000, "num_channels": 1 }, "wake_word": { "enabled": True, "words": ["hello"], "timeout": 30 } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Pipeline - OpenAI + OpenAI + Deepgram const url = 'https://api.meetstream.ai/api/v1/mia'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"agent_name":"V\'s Pipeline Agent","mode":"pipeline","model":{"provider":"openai","model":"gpt-5-nano","system_prompt":"Hi","first_message":"Hi","temperature":0.8,"max_tokens":150,"top_p":1,"frequency_penalty":0,"presence_penalty":0},"voice":{"provider":"openai","model":"tts-1","voice_id":"alloy","speed":1},"transcriber":{"provider":"deepgram","model":"nova-2","language":"en","boostwords":["hey assistant","MeetStream","LiveKit"]},"agent":{"tools":[],"preemptive_generation":false,"user_away_timeout":15,"interruption_mode":"adaptive","interruptions":{"min_duration_seconds":0.5,"word_threshold":2},"false_interruption_timeout":2,"enable_interruptions":true,"resume_false_interruption":true,"turn_detection":"vad","vad_activation_threshold":0.5,"vad_deactivation_threshold":null,"vad_min_silence_duration_ms":550,"vad_min_speech_duration_ms":50,"vad_prefix_padding_duration_ms":300,"endpointing_mode":"fixed","min_endpointing_delay":0.5,"max_endpointing_delay":3},"audio":{"sample_rate":24000,"num_channels":1},"wake_word":{"enabled":true,"words":["hello"],"timeout":30}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Pipeline - OpenAI + OpenAI + Deepgram package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/mia" payload := strings.NewReader("{\n \"agent_name\": \"V's Pipeline Agent\",\n \"mode\": \"pipeline\",\n \"model\": {\n \"provider\": \"openai\",\n \"model\": \"gpt-5-nano\",\n \"system_prompt\": \"Hi\",\n \"first_message\": \"Hi\",\n \"temperature\": 0.8,\n \"max_tokens\": 150,\n \"top_p\": 1,\n \"frequency_penalty\": 0,\n \"presence_penalty\": 0\n },\n \"voice\": {\n \"provider\": \"openai\",\n \"model\": \"tts-1\",\n \"voice_id\": \"alloy\",\n \"speed\": 1\n },\n \"transcriber\": {\n \"provider\": \"deepgram\",\n \"model\": \"nova-2\",\n \"language\": \"en\",\n \"boostwords\": [\n \"hey assistant\",\n \"MeetStream\",\n \"LiveKit\"\n ]\n },\n \"agent\": {\n \"tools\": [],\n \"preemptive_generation\": false,\n \"user_away_timeout\": 15,\n \"interruption_mode\": \"adaptive\",\n \"interruptions\": {\n \"min_duration_seconds\": 0.5,\n \"word_threshold\": 2\n },\n \"false_interruption_timeout\": 2,\n \"enable_interruptions\": true,\n \"resume_false_interruption\": true,\n \"turn_detection\": \"vad\",\n \"vad_activation_threshold\": 0.5,\n \"vad_deactivation_threshold\": null,\n \"vad_min_silence_duration_ms\": 550,\n \"vad_min_speech_duration_ms\": 50,\n \"vad_prefix_padding_duration_ms\": 300,\n \"endpointing_mode\": \"fixed\",\n \"min_endpointing_delay\": 0.5,\n \"max_endpointing_delay\": 3\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n },\n \"wake_word\": {\n \"enabled\": true,\n \"words\": [\n \"hello\"\n ],\n \"timeout\": 30\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Pipeline - OpenAI + OpenAI + Deepgram require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/mia") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"agent_name\": \"V's Pipeline Agent\",\n \"mode\": \"pipeline\",\n \"model\": {\n \"provider\": \"openai\",\n \"model\": \"gpt-5-nano\",\n \"system_prompt\": \"Hi\",\n \"first_message\": \"Hi\",\n \"temperature\": 0.8,\n \"max_tokens\": 150,\n \"top_p\": 1,\n \"frequency_penalty\": 0,\n \"presence_penalty\": 0\n },\n \"voice\": {\n \"provider\": \"openai\",\n \"model\": \"tts-1\",\n \"voice_id\": \"alloy\",\n \"speed\": 1\n },\n \"transcriber\": {\n \"provider\": \"deepgram\",\n \"model\": \"nova-2\",\n \"language\": \"en\",\n \"boostwords\": [\n \"hey assistant\",\n \"MeetStream\",\n \"LiveKit\"\n ]\n },\n \"agent\": {\n \"tools\": [],\n \"preemptive_generation\": false,\n \"user_away_timeout\": 15,\n \"interruption_mode\": \"adaptive\",\n \"interruptions\": {\n \"min_duration_seconds\": 0.5,\n \"word_threshold\": 2\n },\n \"false_interruption_timeout\": 2,\n \"enable_interruptions\": true,\n \"resume_false_interruption\": true,\n \"turn_detection\": \"vad\",\n \"vad_activation_threshold\": 0.5,\n \"vad_deactivation_threshold\": null,\n \"vad_min_silence_duration_ms\": 550,\n \"vad_min_speech_duration_ms\": 50,\n \"vad_prefix_padding_duration_ms\": 300,\n \"endpointing_mode\": \"fixed\",\n \"min_endpointing_delay\": 0.5,\n \"max_endpointing_delay\": 3\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n },\n \"wake_word\": {\n \"enabled\": true,\n \"words\": [\n \"hello\"\n ],\n \"timeout\": 30\n }\n}" response = http.request(request) puts response.read_body ``` ```java Pipeline - OpenAI + OpenAI + Deepgram import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/mia") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"agent_name\": \"V's Pipeline Agent\",\n \"mode\": \"pipeline\",\n \"model\": {\n \"provider\": \"openai\",\n \"model\": \"gpt-5-nano\",\n \"system_prompt\": \"Hi\",\n \"first_message\": \"Hi\",\n \"temperature\": 0.8,\n \"max_tokens\": 150,\n \"top_p\": 1,\n \"frequency_penalty\": 0,\n \"presence_penalty\": 0\n },\n \"voice\": {\n \"provider\": \"openai\",\n \"model\": \"tts-1\",\n \"voice_id\": \"alloy\",\n \"speed\": 1\n },\n \"transcriber\": {\n \"provider\": \"deepgram\",\n \"model\": \"nova-2\",\n \"language\": \"en\",\n \"boostwords\": [\n \"hey assistant\",\n \"MeetStream\",\n \"LiveKit\"\n ]\n },\n \"agent\": {\n \"tools\": [],\n \"preemptive_generation\": false,\n \"user_away_timeout\": 15,\n \"interruption_mode\": \"adaptive\",\n \"interruptions\": {\n \"min_duration_seconds\": 0.5,\n \"word_threshold\": 2\n },\n \"false_interruption_timeout\": 2,\n \"enable_interruptions\": true,\n \"resume_false_interruption\": true,\n \"turn_detection\": \"vad\",\n \"vad_activation_threshold\": 0.5,\n \"vad_deactivation_threshold\": null,\n \"vad_min_silence_duration_ms\": 550,\n \"vad_min_speech_duration_ms\": 50,\n \"vad_prefix_padding_duration_ms\": 300,\n \"endpointing_mode\": \"fixed\",\n \"min_endpointing_delay\": 0.5,\n \"max_endpointing_delay\": 3\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n },\n \"wake_word\": {\n \"enabled\": true,\n \"words\": [\n \"hello\"\n ],\n \"timeout\": 30\n }\n}") .asString(); ``` ```php Pipeline - OpenAI + OpenAI + Deepgram request('POST', 'https://api.meetstream.ai/api/v1/mia', [ 'body' => '{ "agent_name": "V\'s Pipeline Agent", "mode": "pipeline", "model": { "provider": "openai", "model": "gpt-5-nano", "system_prompt": "Hi", "first_message": "Hi", "temperature": 0.8, "max_tokens": 150, "top_p": 1, "frequency_penalty": 0, "presence_penalty": 0 }, "voice": { "provider": "openai", "model": "tts-1", "voice_id": "alloy", "speed": 1 }, "transcriber": { "provider": "deepgram", "model": "nova-2", "language": "en", "boostwords": [ "hey assistant", "MeetStream", "LiveKit" ] }, "agent": { "tools": [], "preemptive_generation": false, "user_away_timeout": 15, "interruption_mode": "adaptive", "interruptions": { "min_duration_seconds": 0.5, "word_threshold": 2 }, "false_interruption_timeout": 2, "enable_interruptions": true, "resume_false_interruption": true, "turn_detection": "vad", "vad_activation_threshold": 0.5, "vad_deactivation_threshold": null, "vad_min_silence_duration_ms": 550, "vad_min_speech_duration_ms": 50, "vad_prefix_padding_duration_ms": 300, "endpointing_mode": "fixed", "min_endpointing_delay": 0.5, "max_endpointing_delay": 3 }, "audio": { "sample_rate": 24000, "num_channels": 1 }, "wake_word": { "enabled": true, "words": [ "hello" ], "timeout": 30 } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Pipeline - OpenAI + OpenAI + Deepgram using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/mia"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"agent_name\": \"V's Pipeline Agent\",\n \"mode\": \"pipeline\",\n \"model\": {\n \"provider\": \"openai\",\n \"model\": \"gpt-5-nano\",\n \"system_prompt\": \"Hi\",\n \"first_message\": \"Hi\",\n \"temperature\": 0.8,\n \"max_tokens\": 150,\n \"top_p\": 1,\n \"frequency_penalty\": 0,\n \"presence_penalty\": 0\n },\n \"voice\": {\n \"provider\": \"openai\",\n \"model\": \"tts-1\",\n \"voice_id\": \"alloy\",\n \"speed\": 1\n },\n \"transcriber\": {\n \"provider\": \"deepgram\",\n \"model\": \"nova-2\",\n \"language\": \"en\",\n \"boostwords\": [\n \"hey assistant\",\n \"MeetStream\",\n \"LiveKit\"\n ]\n },\n \"agent\": {\n \"tools\": [],\n \"preemptive_generation\": false,\n \"user_away_timeout\": 15,\n \"interruption_mode\": \"adaptive\",\n \"interruptions\": {\n \"min_duration_seconds\": 0.5,\n \"word_threshold\": 2\n },\n \"false_interruption_timeout\": 2,\n \"enable_interruptions\": true,\n \"resume_false_interruption\": true,\n \"turn_detection\": \"vad\",\n \"vad_activation_threshold\": 0.5,\n \"vad_deactivation_threshold\": null,\n \"vad_min_silence_duration_ms\": 550,\n \"vad_min_speech_duration_ms\": 50,\n \"vad_prefix_padding_duration_ms\": 300,\n \"endpointing_mode\": \"fixed\",\n \"min_endpointing_delay\": 0.5,\n \"max_endpointing_delay\": 3\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n },\n \"wake_word\": {\n \"enabled\": true,\n \"words\": [\n \"hello\"\n ],\n \"timeout\": 30\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Pipeline - OpenAI + OpenAI + Deepgram import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "agent_name": "V's Pipeline Agent", "mode": "pipeline", "model": [ "provider": "openai", "model": "gpt-5-nano", "system_prompt": "Hi", "first_message": "Hi", "temperature": 0.8, "max_tokens": 150, "top_p": 1, "frequency_penalty": 0, "presence_penalty": 0 ], "voice": [ "provider": "openai", "model": "tts-1", "voice_id": "alloy", "speed": 1 ], "transcriber": [ "provider": "deepgram", "model": "nova-2", "language": "en", "boostwords": ["hey assistant", "MeetStream", "LiveKit"] ], "agent": [ "tools": [], "preemptive_generation": false, "user_away_timeout": 15, "interruption_mode": "adaptive", "interruptions": [ "min_duration_seconds": 0.5, "word_threshold": 2 ], "false_interruption_timeout": 2, "enable_interruptions": true, "resume_false_interruption": true, "turn_detection": "vad", "vad_activation_threshold": 0.5, "vad_deactivation_threshold": , "vad_min_silence_duration_ms": 550, "vad_min_speech_duration_ms": 50, "vad_prefix_padding_duration_ms": 300, "endpointing_mode": "fixed", "min_endpointing_delay": 0.5, "max_endpointing_delay": 3 ], "audio": [ "sample_rate": 24000, "num_channels": 1 ], "wake_word": [ "enabled": true, "words": ["hello"], "timeout": 30 ] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/mia")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` ### Realtime - Avatar (Anam) **Request** ```json { "agent_name": "New MIA Agent", "mode": "realtime", "model": { "provider": "openai", "model": "gpt-realtime-mini", "system_prompt": "You are a helpful AI meeting assistant. Keep responses concise and natural. Listen actively and provide value to the conversation.", "first_message": "Hey there! I am your AI assistant, ready to help with your meeting.", "temperature": 0.8, "voice": "coral", "modalities": [ "text", "audio" ], "max_response_output_tokens": 200 }, "voice": null, "transcriber": null, "agent": { "tools": [], "preemptive_generation": false, "user_away_timeout": 15, "interruptions": { "min_duration_seconds": 0.5, "word_threshold": 0 }, "false_interruption_timeout": 2, "vad_type": "server_vad", "enable_interruptions": true, "resume_false_interruption": true, "tools_enabled": false, "vad_threshold": 0.5, "vad_prefix_padding_ms": 0, "vad_silence_duration_ms": 200 }, "audio": { "sample_rate": 24000, "num_channels": 1 }, "wake_word": null, "Avatar": { "provider": "anam", "enabled": true, "avatar_id": "2644bbca-ee18-4f66-9e84-9360043c9883" } } ``` **Response** ```json { "message": "Agent configuration created successfully.", "agent_config_id": "d4e5f6a7-b8c9-0123-defa-234567890123", "agent_config": { "AgentConfigID": "d4e5f6a7-b8c9-0123-defa-234567890123", "UserID": "34e86428-0081-70e8-5bfb-3fa111d0ff77", "AgentName": "New MIA Agent", "Mode": "realtime", "Model": { "provider": "openai", "model": "gpt-realtime-mini", "system_prompt": "You are a helpful AI meeting assistant. Keep responses concise and natural. Listen actively and provide value to the conversation.", "first_message": "Hey there! I am your AI assistant, ready to help with your meeting.", "temperature": 0.8, "voice": "coral", "modalities": [ "text", "audio" ], "max_response_output_tokens": 200 }, "Voice": null, "Transcriber": null, "Agent": { "tools": [], "preemptive_generation": false, "user_away_timeout": 15, "interruptions": { "min_duration_seconds": 0.5, "word_threshold": 0 }, "false_interruption_timeout": 2, "vad_type": "server_vad", "enable_interruptions": true, "resume_false_interruption": true, "tools_enabled": false, "vad_threshold": 0.5, "vad_prefix_padding_ms": 0, "vad_silence_duration_ms": 200 }, "Audio": { "sample_rate": 24000, "num_channels": 1 }, "Avatar": { "provider": "anam", "enabled": true, "avatar_id": "2644bbca-ee18-4f66-9e84-9360043c9883" }, "WakeWord": null, "CreatedAt": "2026-04-24T12:40:00Z", "UpdatedAt": "2026-04-24T12:40:00Z" } } ``` **SDK Code** ```python Realtime - Avatar (Anam) import requests url = "https://api.meetstream.ai/api/v1/mia" payload = { "agent_name": "New MIA Agent", "mode": "realtime", "model": { "provider": "openai", "model": "gpt-realtime-mini", "system_prompt": "You are a helpful AI meeting assistant. Keep responses concise and natural. Listen actively and provide value to the conversation.", "first_message": "Hey there! I am your AI assistant, ready to help with your meeting.", "temperature": 0.8, "voice": "coral", "modalities": ["text", "audio"], "max_response_output_tokens": 200 }, "voice": None, "transcriber": None, "agent": { "tools": [], "preemptive_generation": False, "user_away_timeout": 15, "interruptions": { "min_duration_seconds": 0.5, "word_threshold": 0 }, "false_interruption_timeout": 2, "vad_type": "server_vad", "enable_interruptions": True, "resume_false_interruption": True, "tools_enabled": False, "vad_threshold": 0.5, "vad_prefix_padding_ms": 0, "vad_silence_duration_ms": 200 }, "audio": { "sample_rate": 24000, "num_channels": 1 }, "wake_word": None, "Avatar": { "provider": "anam", "enabled": True, "avatar_id": "2644bbca-ee18-4f66-9e84-9360043c9883" } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.post(url, json=payload, headers=headers) print(response.json()) ``` ```javascript Realtime - Avatar (Anam) const url = 'https://api.meetstream.ai/api/v1/mia'; const options = { method: 'POST', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"agent_name":"New MIA Agent","mode":"realtime","model":{"provider":"openai","model":"gpt-realtime-mini","system_prompt":"You are a helpful AI meeting assistant. Keep responses concise and natural. Listen actively and provide value to the conversation.","first_message":"Hey there! I am your AI assistant, ready to help with your meeting.","temperature":0.8,"voice":"coral","modalities":["text","audio"],"max_response_output_tokens":200},"voice":null,"transcriber":null,"agent":{"tools":[],"preemptive_generation":false,"user_away_timeout":15,"interruptions":{"min_duration_seconds":0.5,"word_threshold":0},"false_interruption_timeout":2,"vad_type":"server_vad","enable_interruptions":true,"resume_false_interruption":true,"tools_enabled":false,"vad_threshold":0.5,"vad_prefix_padding_ms":0,"vad_silence_duration_ms":200},"audio":{"sample_rate":24000,"num_channels":1},"wake_word":null,"Avatar":{"provider":"anam","enabled":true,"avatar_id":"2644bbca-ee18-4f66-9e84-9360043c9883"}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go Realtime - Avatar (Anam) package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/mia" payload := strings.NewReader("{\n \"agent_name\": \"New MIA Agent\",\n \"mode\": \"realtime\",\n \"model\": {\n \"provider\": \"openai\",\n \"model\": \"gpt-realtime-mini\",\n \"system_prompt\": \"You are a helpful AI meeting assistant. Keep responses concise and natural. Listen actively and provide value to the conversation.\",\n \"first_message\": \"Hey there! I am your AI assistant, ready to help with your meeting.\",\n \"temperature\": 0.8,\n \"voice\": \"coral\",\n \"modalities\": [\n \"text\",\n \"audio\"\n ],\n \"max_response_output_tokens\": 200\n },\n \"voice\": null,\n \"transcriber\": null,\n \"agent\": {\n \"tools\": [],\n \"preemptive_generation\": false,\n \"user_away_timeout\": 15,\n \"interruptions\": {\n \"min_duration_seconds\": 0.5,\n \"word_threshold\": 0\n },\n \"false_interruption_timeout\": 2,\n \"vad_type\": \"server_vad\",\n \"enable_interruptions\": true,\n \"resume_false_interruption\": true,\n \"tools_enabled\": false,\n \"vad_threshold\": 0.5,\n \"vad_prefix_padding_ms\": 0,\n \"vad_silence_duration_ms\": 200\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n },\n \"wake_word\": null,\n \"Avatar\": {\n \"provider\": \"anam\",\n \"enabled\": true,\n \"avatar_id\": \"2644bbca-ee18-4f66-9e84-9360043c9883\"\n }\n}") req, _ := http.NewRequest("POST", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby Realtime - Avatar (Anam) require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/mia") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Post.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"agent_name\": \"New MIA Agent\",\n \"mode\": \"realtime\",\n \"model\": {\n \"provider\": \"openai\",\n \"model\": \"gpt-realtime-mini\",\n \"system_prompt\": \"You are a helpful AI meeting assistant. Keep responses concise and natural. Listen actively and provide value to the conversation.\",\n \"first_message\": \"Hey there! I am your AI assistant, ready to help with your meeting.\",\n \"temperature\": 0.8,\n \"voice\": \"coral\",\n \"modalities\": [\n \"text\",\n \"audio\"\n ],\n \"max_response_output_tokens\": 200\n },\n \"voice\": null,\n \"transcriber\": null,\n \"agent\": {\n \"tools\": [],\n \"preemptive_generation\": false,\n \"user_away_timeout\": 15,\n \"interruptions\": {\n \"min_duration_seconds\": 0.5,\n \"word_threshold\": 0\n },\n \"false_interruption_timeout\": 2,\n \"vad_type\": \"server_vad\",\n \"enable_interruptions\": true,\n \"resume_false_interruption\": true,\n \"tools_enabled\": false,\n \"vad_threshold\": 0.5,\n \"vad_prefix_padding_ms\": 0,\n \"vad_silence_duration_ms\": 200\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n },\n \"wake_word\": null,\n \"Avatar\": {\n \"provider\": \"anam\",\n \"enabled\": true,\n \"avatar_id\": \"2644bbca-ee18-4f66-9e84-9360043c9883\"\n }\n}" response = http.request(request) puts response.read_body ``` ```java Realtime - Avatar (Anam) import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.post("https://api.meetstream.ai/api/v1/mia") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"agent_name\": \"New MIA Agent\",\n \"mode\": \"realtime\",\n \"model\": {\n \"provider\": \"openai\",\n \"model\": \"gpt-realtime-mini\",\n \"system_prompt\": \"You are a helpful AI meeting assistant. Keep responses concise and natural. Listen actively and provide value to the conversation.\",\n \"first_message\": \"Hey there! I am your AI assistant, ready to help with your meeting.\",\n \"temperature\": 0.8,\n \"voice\": \"coral\",\n \"modalities\": [\n \"text\",\n \"audio\"\n ],\n \"max_response_output_tokens\": 200\n },\n \"voice\": null,\n \"transcriber\": null,\n \"agent\": {\n \"tools\": [],\n \"preemptive_generation\": false,\n \"user_away_timeout\": 15,\n \"interruptions\": {\n \"min_duration_seconds\": 0.5,\n \"word_threshold\": 0\n },\n \"false_interruption_timeout\": 2,\n \"vad_type\": \"server_vad\",\n \"enable_interruptions\": true,\n \"resume_false_interruption\": true,\n \"tools_enabled\": false,\n \"vad_threshold\": 0.5,\n \"vad_prefix_padding_ms\": 0,\n \"vad_silence_duration_ms\": 200\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n },\n \"wake_word\": null,\n \"Avatar\": {\n \"provider\": \"anam\",\n \"enabled\": true,\n \"avatar_id\": \"2644bbca-ee18-4f66-9e84-9360043c9883\"\n }\n}") .asString(); ``` ```php Realtime - Avatar (Anam) request('POST', 'https://api.meetstream.ai/api/v1/mia', [ 'body' => '{ "agent_name": "New MIA Agent", "mode": "realtime", "model": { "provider": "openai", "model": "gpt-realtime-mini", "system_prompt": "You are a helpful AI meeting assistant. Keep responses concise and natural. Listen actively and provide value to the conversation.", "first_message": "Hey there! I am your AI assistant, ready to help with your meeting.", "temperature": 0.8, "voice": "coral", "modalities": [ "text", "audio" ], "max_response_output_tokens": 200 }, "voice": null, "transcriber": null, "agent": { "tools": [], "preemptive_generation": false, "user_away_timeout": 15, "interruptions": { "min_duration_seconds": 0.5, "word_threshold": 0 }, "false_interruption_timeout": 2, "vad_type": "server_vad", "enable_interruptions": true, "resume_false_interruption": true, "tools_enabled": false, "vad_threshold": 0.5, "vad_prefix_padding_ms": 0, "vad_silence_duration_ms": 200 }, "audio": { "sample_rate": 24000, "num_channels": 1 }, "wake_word": null, "Avatar": { "provider": "anam", "enabled": true, "avatar_id": "2644bbca-ee18-4f66-9e84-9360043c9883" } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp Realtime - Avatar (Anam) using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/mia"); var request = new RestRequest(Method.POST); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"agent_name\": \"New MIA Agent\",\n \"mode\": \"realtime\",\n \"model\": {\n \"provider\": \"openai\",\n \"model\": \"gpt-realtime-mini\",\n \"system_prompt\": \"You are a helpful AI meeting assistant. Keep responses concise and natural. Listen actively and provide value to the conversation.\",\n \"first_message\": \"Hey there! I am your AI assistant, ready to help with your meeting.\",\n \"temperature\": 0.8,\n \"voice\": \"coral\",\n \"modalities\": [\n \"text\",\n \"audio\"\n ],\n \"max_response_output_tokens\": 200\n },\n \"voice\": null,\n \"transcriber\": null,\n \"agent\": {\n \"tools\": [],\n \"preemptive_generation\": false,\n \"user_away_timeout\": 15,\n \"interruptions\": {\n \"min_duration_seconds\": 0.5,\n \"word_threshold\": 0\n },\n \"false_interruption_timeout\": 2,\n \"vad_type\": \"server_vad\",\n \"enable_interruptions\": true,\n \"resume_false_interruption\": true,\n \"tools_enabled\": false,\n \"vad_threshold\": 0.5,\n \"vad_prefix_padding_ms\": 0,\n \"vad_silence_duration_ms\": 200\n },\n \"audio\": {\n \"sample_rate\": 24000,\n \"num_channels\": 1\n },\n \"wake_word\": null,\n \"Avatar\": {\n \"provider\": \"anam\",\n \"enabled\": true,\n \"avatar_id\": \"2644bbca-ee18-4f66-9e84-9360043c9883\"\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift Realtime - Avatar (Anam) import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "agent_name": "New MIA Agent", "mode": "realtime", "model": [ "provider": "openai", "model": "gpt-realtime-mini", "system_prompt": "You are a helpful AI meeting assistant. Keep responses concise and natural. Listen actively and provide value to the conversation.", "first_message": "Hey there! I am your AI assistant, ready to help with your meeting.", "temperature": 0.8, "voice": "coral", "modalities": ["text", "audio"], "max_response_output_tokens": 200 ], "voice": , "transcriber": , "agent": [ "tools": [], "preemptive_generation": false, "user_away_timeout": 15, "interruptions": [ "min_duration_seconds": 0.5, "word_threshold": 0 ], "false_interruption_timeout": 2, "vad_type": "server_vad", "enable_interruptions": true, "resume_false_interruption": true, "tools_enabled": false, "vad_threshold": 0.5, "vad_prefix_padding_ms": 0, "vad_silence_duration_ms": 200 ], "audio": [ "sample_rate": 24000, "num_channels": 1 ], "wake_word": , "Avatar": [ "provider": "anam", "enabled": true, "avatar_id": "2644bbca-ee18-4f66-9e84-9360043c9883" ] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/mia")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "POST" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Update Agent Config PUT https://api.meetstream.ai/api/v1/mia Content-Type: application/json Reference: https://docs.meetstream.ai/api-reference/api-endpoints/mia/update-agent-config ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/mia: put: operationId: update-agent-config summary: Update Agent Config tags: - subpackage_mia parameters: - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/MIAUpdateConfigResponse' requestBody: content: application/json: schema: $ref: '#/components/schemas/MIAUpdateConfigRequest' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: MiaUpdateConfigRequestMode: type: string enum: - pipeline - realtime title: MiaUpdateConfigRequestMode MiaModelConfigThinkingConfig: type: object properties: include_thoughts: type: boolean thinking_budget: type: integer title: MiaModelConfigThinkingConfig MIAModelConfig: type: object properties: provider: type: string model: type: string system_prompt: type: string first_message: type: string max_token: type: integer temperature: type: number format: double voice: type: string description: Used for realtime mode models that have a built-in voice. modalities: type: array items: type: string max_response_output_tokens: type: integer max_tokens: type: integer top_p: type: number format: double frequency_penalty: type: number format: double presence_penalty: type: number format: double thinking_config: $ref: '#/components/schemas/MiaModelConfigThinkingConfig' enable_affective_dialog: type: boolean proactivity: type: boolean disable_automatic_activity_detection: type: boolean title: MIAModelConfig MIAVoiceConfig: type: object properties: provider: type: string model: type: string voice_id: type: string speed: type: number format: double title: MIAVoiceConfig MIATranscriberConfig: type: object properties: provider: type: string model: type: string language: type: string boostwords: type: array items: type: string title: MIATranscriberConfig MiaAgentConfigInterruptionMode: type: string enum: - adaptive - vad title: MiaAgentConfigInterruptionMode MIAInterruptionsConfig: type: object properties: min_duration_seconds: type: number format: double word_threshold: type: integer title: MIAInterruptionsConfig MIAAgentConfig: type: object properties: tools: type: array items: type: string preemptive_generation: type: boolean user_away_timeout: type: number format: double interruption_mode: $ref: '#/components/schemas/MiaAgentConfigInterruptionMode' interruptions: $ref: '#/components/schemas/MIAInterruptionsConfig' false_interruption_timeout: type: number format: double vad_eagerness: type: string vad_type: type: string enable_interruptions: type: boolean resume_false_interruption: type: boolean response_modality: type: string mcp_servers: type: object additionalProperties: description: Any type tools_enabled: type: boolean vad_threshold: type: number format: double vad_prefix_padding_ms: type: integer vad_silence_duration_ms: type: integer turn_detection: type: string vad_activation_threshold: type: number format: double vad_deactivation_threshold: type: - number - 'null' format: double vad_min_silence_duration_ms: type: integer vad_min_speech_duration_ms: type: integer vad_prefix_padding_duration_ms: type: integer endpointing_mode: type: string min_endpointing_delay: type: number format: double max_endpointing_delay: type: number format: double title: MIAAgentConfig MIAAudioConfig: type: object properties: sample_rate: type: integer num_channels: type: integer title: MIAAudioConfig MIAUpdateConfigRequest: type: object properties: agent_config_id: type: string agent_name: type: string mode: $ref: '#/components/schemas/MiaUpdateConfigRequestMode' model: $ref: '#/components/schemas/MIAModelConfig' voice: $ref: '#/components/schemas/MIAVoiceConfig' transcriber: $ref: '#/components/schemas/MIATranscriberConfig' agent: $ref: '#/components/schemas/MIAAgentConfig' audio: $ref: '#/components/schemas/MIAAudioConfig' required: - agent_config_id title: MIAUpdateConfigRequest MiaAgentConfigObjectMode: type: string enum: - pipeline - realtime title: MiaAgentConfigObjectMode MIAAvatarConfig: type: object properties: provider: type: string enabled: type: boolean avatar_id: type: string title: MIAAvatarConfig MIAAgentConfigObject: type: object properties: AgentConfigID: type: string UserID: type: string AgentName: type: string Mode: $ref: '#/components/schemas/MiaAgentConfigObjectMode' Model: $ref: '#/components/schemas/MIAModelConfig' Voice: oneOf: - $ref: '#/components/schemas/MIAVoiceConfig' - type: 'null' Transcriber: oneOf: - $ref: '#/components/schemas/MIATranscriberConfig' - type: 'null' Agent: $ref: '#/components/schemas/MIAAgentConfig' Audio: $ref: '#/components/schemas/MIAAudioConfig' Avatar: $ref: '#/components/schemas/MIAAvatarConfig' WakeWord: type: object additionalProperties: description: Any type CreatedAt: type: string UpdatedAt: type: string description: A full agent configuration object as returned by the API. title: MIAAgentConfigObject MIAUpdateConfigResponse: type: object properties: message: type: string agent_config: $ref: '#/components/schemas/MIAAgentConfigObject' title: MIAUpdateConfigResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Request** ```json { "agent_config_id": "d3ffa96f-85be-4037-81d0-a8eeb80c4876", "agent": { "interruption_mode": "adaptive", "interruptions": { "min_duration_seconds": 0.8, "word_threshold": 3 } } } ``` **Response** ```json { "message": "Agent configuration updated successfully.", "agent_config": { "AgentConfigID": "d3ffa96f-85be-4037-81d0-a8eeb80c4876", "UserID": "34e86428-0081-70e8-5bfb-3fa111d0ff77", "AgentName": "MeetStream's agent", "Mode": "pipeline", "Model": { "provider": "openai", "model": "gpt-4o-mini", "system_prompt": "You are a helpful meeting assistant. Keep responses concise and friendly. Speak naturally and conversationally.", "first_message": "Hi! I'm your AI meeting assistant. How can I help you today?", "max_token": 2048, "temperature": 0.8 }, "Voice": { "provider": "elevenlabs", "model": "tts-1", "voice_id": "alloy" }, "Transcriber": { "provider": "openai", "model": "whisper-1", "language": "en", "boostwords": [] }, "Agent": { "interruption_mode": "adaptive", "interruptions": { "min_duration_seconds": 0.8, "word_threshold": 3 } }, "Audio": { "sample_rate": 24000, "num_channels": 1 }, "CreatedAt": "2026-04-24T11:31:09Z", "UpdatedAt": "2026-04-24T11:54:34Z" } } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/mia" payload = { "agent_config_id": "d3ffa96f-85be-4037-81d0-a8eeb80c4876", "agent": { "interruption_mode": "adaptive", "interruptions": { "min_duration_seconds": 0.8, "word_threshold": 3 } } } headers = { "Authorization": "", "Content-Type": "application/json" } response = requests.put(url, json=payload, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/mia'; const options = { method: 'PUT', headers: {Authorization: '', 'Content-Type': 'application/json'}, body: '{"agent_config_id":"d3ffa96f-85be-4037-81d0-a8eeb80c4876","agent":{"interruption_mode":"adaptive","interruptions":{"min_duration_seconds":0.8,"word_threshold":3}}}' }; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "strings" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/mia" payload := strings.NewReader("{\n \"agent_config_id\": \"d3ffa96f-85be-4037-81d0-a8eeb80c4876\",\n \"agent\": {\n \"interruption_mode\": \"adaptive\",\n \"interruptions\": {\n \"min_duration_seconds\": 0.8,\n \"word_threshold\": 3\n }\n }\n}") req, _ := http.NewRequest("PUT", url, payload) req.Header.Add("Authorization", "") req.Header.Add("Content-Type", "application/json") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/mia") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Put.new(url) request["Authorization"] = '' request["Content-Type"] = 'application/json' request.body = "{\n \"agent_config_id\": \"d3ffa96f-85be-4037-81d0-a8eeb80c4876\",\n \"agent\": {\n \"interruption_mode\": \"adaptive\",\n \"interruptions\": {\n \"min_duration_seconds\": 0.8,\n \"word_threshold\": 3\n }\n }\n}" response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.put("https://api.meetstream.ai/api/v1/mia") .header("Authorization", "") .header("Content-Type", "application/json") .body("{\n \"agent_config_id\": \"d3ffa96f-85be-4037-81d0-a8eeb80c4876\",\n \"agent\": {\n \"interruption_mode\": \"adaptive\",\n \"interruptions\": {\n \"min_duration_seconds\": 0.8,\n \"word_threshold\": 3\n }\n }\n}") .asString(); ``` ```php request('PUT', 'https://api.meetstream.ai/api/v1/mia', [ 'body' => '{ "agent_config_id": "d3ffa96f-85be-4037-81d0-a8eeb80c4876", "agent": { "interruption_mode": "adaptive", "interruptions": { "min_duration_seconds": 0.8, "word_threshold": 3 } } }', 'headers' => [ 'Authorization' => '', 'Content-Type' => 'application/json', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/mia"); var request = new RestRequest(Method.PUT); request.AddHeader("Authorization", ""); request.AddHeader("Content-Type", "application/json"); request.AddParameter("application/json", "{\n \"agent_config_id\": \"d3ffa96f-85be-4037-81d0-a8eeb80c4876\",\n \"agent\": {\n \"interruption_mode\": \"adaptive\",\n \"interruptions\": {\n \"min_duration_seconds\": 0.8,\n \"word_threshold\": 3\n }\n }\n}", ParameterType.RequestBody); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = [ "Authorization": "", "Content-Type": "application/json" ] let parameters = [ "agent_config_id": "d3ffa96f-85be-4037-81d0-a8eeb80c4876", "agent": [ "interruption_mode": "adaptive", "interruptions": [ "min_duration_seconds": 0.8, "word_threshold": 3 ] ] ] as [String : Any] let postData = JSONSerialization.data(withJSONObject: parameters, options: []) let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/mia")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "PUT" request.allHTTPHeaderFields = headers request.httpBody = postData as Data let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Get Agent Configs GET https://api.meetstream.ai/api/v1/mia Reference: https://docs.meetstream.ai/api-reference/api-endpoints/mia/get-agent-configs ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/mia: get: operationId: get-agent-configs summary: Get Agent Configs tags: - subpackage_mia parameters: - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/MIAGetConfigsResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: MiaAgentConfigObjectMode: type: string enum: - pipeline - realtime title: MiaAgentConfigObjectMode MiaModelConfigThinkingConfig: type: object properties: include_thoughts: type: boolean thinking_budget: type: integer title: MiaModelConfigThinkingConfig MIAModelConfig: type: object properties: provider: type: string model: type: string system_prompt: type: string first_message: type: string max_token: type: integer temperature: type: number format: double voice: type: string description: Used for realtime mode models that have a built-in voice. modalities: type: array items: type: string max_response_output_tokens: type: integer max_tokens: type: integer top_p: type: number format: double frequency_penalty: type: number format: double presence_penalty: type: number format: double thinking_config: $ref: '#/components/schemas/MiaModelConfigThinkingConfig' enable_affective_dialog: type: boolean proactivity: type: boolean disable_automatic_activity_detection: type: boolean title: MIAModelConfig MIAVoiceConfig: type: object properties: provider: type: string model: type: string voice_id: type: string speed: type: number format: double title: MIAVoiceConfig MIATranscriberConfig: type: object properties: provider: type: string model: type: string language: type: string boostwords: type: array items: type: string title: MIATranscriberConfig MiaAgentConfigInterruptionMode: type: string enum: - adaptive - vad title: MiaAgentConfigInterruptionMode MIAInterruptionsConfig: type: object properties: min_duration_seconds: type: number format: double word_threshold: type: integer title: MIAInterruptionsConfig MIAAgentConfig: type: object properties: tools: type: array items: type: string preemptive_generation: type: boolean user_away_timeout: type: number format: double interruption_mode: $ref: '#/components/schemas/MiaAgentConfigInterruptionMode' interruptions: $ref: '#/components/schemas/MIAInterruptionsConfig' false_interruption_timeout: type: number format: double vad_eagerness: type: string vad_type: type: string enable_interruptions: type: boolean resume_false_interruption: type: boolean response_modality: type: string mcp_servers: type: object additionalProperties: description: Any type tools_enabled: type: boolean vad_threshold: type: number format: double vad_prefix_padding_ms: type: integer vad_silence_duration_ms: type: integer turn_detection: type: string vad_activation_threshold: type: number format: double vad_deactivation_threshold: type: - number - 'null' format: double vad_min_silence_duration_ms: type: integer vad_min_speech_duration_ms: type: integer vad_prefix_padding_duration_ms: type: integer endpointing_mode: type: string min_endpointing_delay: type: number format: double max_endpointing_delay: type: number format: double title: MIAAgentConfig MIAAudioConfig: type: object properties: sample_rate: type: integer num_channels: type: integer title: MIAAudioConfig MIAAvatarConfig: type: object properties: provider: type: string enabled: type: boolean avatar_id: type: string title: MIAAvatarConfig MIAAgentConfigObject: type: object properties: AgentConfigID: type: string UserID: type: string AgentName: type: string Mode: $ref: '#/components/schemas/MiaAgentConfigObjectMode' Model: $ref: '#/components/schemas/MIAModelConfig' Voice: oneOf: - $ref: '#/components/schemas/MIAVoiceConfig' - type: 'null' Transcriber: oneOf: - $ref: '#/components/schemas/MIATranscriberConfig' - type: 'null' Agent: $ref: '#/components/schemas/MIAAgentConfig' Audio: $ref: '#/components/schemas/MIAAudioConfig' Avatar: $ref: '#/components/schemas/MIAAvatarConfig' WakeWord: type: object additionalProperties: description: Any type CreatedAt: type: string UpdatedAt: type: string description: A full agent configuration object as returned by the API. title: MIAAgentConfigObject MIAGetConfigsResponse: type: object properties: agent_configs: type: array items: $ref: '#/components/schemas/MIAAgentConfigObject' count: type: integer title: MIAGetConfigsResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "agent_configs": [ { "AgentConfigID": "786f4da5-dfdb-4572-9294-3174912b5bc1", "UserID": "34e86428-0081-70e8-5bfb-3fa111d0ff77", "AgentName": "MeetStream's agent", "Mode": "pipeline", "Model": { "provider": "openai", "model": "gpt-4o-mini", "system_prompt": "You are a helpful meeting assistant. Keep responses concise and friendly. Speak naturally and conversationally.", "first_message": "Hi! I'm your AI meeting assistant. How can I help you today?", "max_token": 2048, "temperature": 0.8 }, "Voice": { "provider": "elevenlabs", "model": "tts-1", "voice_id": "alloy" }, "Transcriber": { "provider": "openai", "model": "whisper-1", "language": "en", "boostwords": [] }, "Agent": { "tools": [ "current_time", "weather_now" ], "preemptive_generation": true, "user_away_timeout": 15 }, "Audio": { "sample_rate": 24000, "num_channels": 1 }, "CreatedAt": "2026-04-24T11:26:12Z", "UpdatedAt": "2026-04-24T11:26:12Z" }, { "AgentConfigID": "f64aeb25-0b8c-478f-ae4a-f0f6438be32d", "UserID": "34e86428-0081-70e8-5bfb-3fa111d0ff77", "AgentName": "MIA Realtime Agent", "Mode": "realtime", "Model": { "provider": "openai", "model": "gpt-realtime-mini", "system_prompt": "You are a helpful AI meeting assistant. Keep responses concise and natural. Listen actively and provide value to the conversation.", "temperature": 0.8, "voice": "coral" }, "Voice": null, "Transcriber": null, "Agent": { "preemptive_generation": true, "user_away_timeout": 15, "interruption_mode": "vad", "interruptions": { "min_duration_seconds": 0.5, "word_threshold": 0 }, "false_interruption_timeout": 2, "vad_eagerness": "medium", "vad_type": "semantic_vad", "enable_interruptions": true, "resume_false_interruption": true, "response_modality": "audio", "mcp_servers": {} }, "Audio": { "sample_rate": 24000, "num_channels": 1 }, "Avatar": { "provider": "anam", "enabled": false, "avatar_id": "" }, "WakeWord": {}, "CreatedAt": "2026-04-22T06:33:29Z", "UpdatedAt": "2026-04-22T06:33:29Z" } ], "count": 3 } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/mia" headers = {"Authorization": ""} response = requests.get(url, headers=headers) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/mia'; const options = {method: 'GET', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/mia" req, _ := http.NewRequest("GET", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/mia") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Get.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.get("https://api.meetstream.ai/api/v1/mia") .header("Authorization", "") .asString(); ``` ```php request('GET', 'https://api.meetstream.ai/api/v1/mia', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/mia"); var request = new RestRequest(Method.GET); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/mia")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "GET" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # Delete Agent Config DELETE https://api.meetstream.ai/api/v1/mia Reference: https://docs.meetstream.ai/api-reference/api-endpoints/mia/delete-agent-config ## OpenAPI Specification ```yaml openapi: 3.1.0 info: title: Meetstream API version: 1.0.0 paths: /api/v1/mia: delete: operationId: delete-agent-config summary: Delete Agent Config tags: - subpackage_mia parameters: - name: agent_config_id in: query description: The ID of the agent configuration to delete. required: true schema: type: string - name: Authorization in: header description: 'Format: Token ' required: true schema: type: string responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/MIADeleteConfigResponse' servers: - url: https://api.meetstream.ai description: Meetstream API v1 components: schemas: MIADeleteConfigResponse: type: object properties: message: type: string title: MIADeleteConfigResponse securitySchemes: TokenAuth: type: apiKey in: header name: Authorization description: 'Format: Token ' ``` ## Examples **Response** ```json { "message": "Agent configuration deleted successfully." } ``` **SDK Code** ```python import requests url = "https://api.meetstream.ai/api/v1/mia" querystring = {"agent_config_id":"d3ffa96f-85be-4037-81d0-a8eeb80c4876"} headers = {"Authorization": ""} response = requests.delete(url, headers=headers, params=querystring) print(response.json()) ``` ```javascript const url = 'https://api.meetstream.ai/api/v1/mia?agent_config_id=d3ffa96f-85be-4037-81d0-a8eeb80c4876'; const options = {method: 'DELETE', headers: {Authorization: ''}}; try { const response = await fetch(url, options); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } ``` ```go package main import ( "fmt" "net/http" "io" ) func main() { url := "https://api.meetstream.ai/api/v1/mia?agent_config_id=d3ffa96f-85be-4037-81d0-a8eeb80c4876" req, _ := http.NewRequest("DELETE", url, nil) req.Header.Add("Authorization", "") res, _ := http.DefaultClient.Do(req) defer res.Body.Close() body, _ := io.ReadAll(res.Body) fmt.Println(res) fmt.Println(string(body)) } ``` ```ruby require 'uri' require 'net/http' url = URI("https://api.meetstream.ai/api/v1/mia?agent_config_id=d3ffa96f-85be-4037-81d0-a8eeb80c4876") http = Net::HTTP.new(url.host, url.port) http.use_ssl = true request = Net::HTTP::Delete.new(url) request["Authorization"] = '' response = http.request(request) puts response.read_body ``` ```java import com.mashape.unirest.http.HttpResponse; import com.mashape.unirest.http.Unirest; HttpResponse response = Unirest.delete("https://api.meetstream.ai/api/v1/mia?agent_config_id=d3ffa96f-85be-4037-81d0-a8eeb80c4876") .header("Authorization", "") .asString(); ``` ```php request('DELETE', 'https://api.meetstream.ai/api/v1/mia?agent_config_id=d3ffa96f-85be-4037-81d0-a8eeb80c4876', [ 'headers' => [ 'Authorization' => '', ], ]); echo $response->getBody(); ``` ```csharp using RestSharp; var client = new RestClient("https://api.meetstream.ai/api/v1/mia?agent_config_id=d3ffa96f-85be-4037-81d0-a8eeb80c4876"); var request = new RestRequest(Method.DELETE); request.AddHeader("Authorization", ""); IRestResponse response = client.Execute(request); ``` ```swift import Foundation let headers = ["Authorization": ""] let request = NSMutableURLRequest(url: NSURL(string: "https://api.meetstream.ai/api/v1/mia?agent_config_id=d3ffa96f-85be-4037-81d0-a8eeb80c4876")! as URL, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0) request.httpMethod = "DELETE" request.allHTTPHeaderFields = headers let session = URLSession.shared let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in if (error != nil) { print(error as Any) } else { let httpResponse = response as? HTTPURLResponse print(httpResponse) } }) dataTask.resume() ``` # AI Integrations > Connect MeetStream to AI coding assistants and chat tools using our MCP server and Claude Skill. ## Claude Code Plugin The MeetStream Claude Code Plugin extends Claude Code with native MeetStream knowledge letting you deploy bots, query transcripts, and work with the MeetStream API directly from your terminal without switching context. ### Installation Run the following three commands inside Claude Code to install the plugin: **1. Add the MeetStream marketplace** ``` /plugin marketplace add meetstream-ai/claude-plugin ``` Registers the MeetStream plugin marketplace with Claude Code. This makes MeetStream plugins discoverable — no plugins are installed yet. **2. Install the plugin** ``` /plugin install meetstream@meetstream-ai ``` Installs the MeetStream plugin from the marketplace you just added. **3. Activate the plugin** ``` /reload-plugins ``` Loads the installed plugin into your current Claude Code session. Run this after installation for MeetStream skills and commands to become available. --- ### What's included - Bot lifecycle management via natural language - Live transcription and audio streaming setup - WebSocket bot control commands - Webhook and callback event handling - Google Calendar integration guidance - Ready-to-use API call generation --- > **Tip:** Pair the Claude Code plugin with the [MCP Server](https://docs.meetstream.ai/build-with-ai/ai-integrations#mcp-server) for the most complete experience. The plugin handles common patterns, while the MCP server handles live doc lookups in real time. --- ## MCP Server The MeetStream MCP (Model Context Protocol) server lets AI tools like Cursor, VS Code, Claude Code, and Claude.ai query our live documentation on demand. **MCP Server URL:** ``` https://docs.meetstream.ai/_mcp/server ``` ### Setup Instructions 1. Open **Cursor Settings** → **Features** → **MCP Servers** 2. Click **Add new MCP server** 3. Set **Name** to `meetstream-docs` 4. Set **Type** to `SSE` 5. Set **URL** to `https://docs.meetstream.ai/_mcp/server` 6. Click **Save** and restart Cursor Add to your `.vscode/mcp.json` or user settings: ```json { "servers": { "meetstream-docs": { "type": "sse", "url": "https://docs.meetstream.ai/_mcp/server" } } } ``` Run in your terminal: ```bash claude mcp add meetstream-docs --transport sse https://docs.meetstream.ai/_mcp/server ``` 1. Go to **claude.ai** → Click your profile → **Settings** 2. Navigate to **Integrations** → **Add integration** 3. Enter the MCP URL: `https://docs.meetstream.ai/_mcp/server` 4. Save and enable the integration Once connected, your AI assistant can search and reference MeetStream documentation in real time during your sessions. --- ## Recommended Setup For the best developer experience, use **both together**: | Tool | Purpose | | ---------------- | ---------------------------------------------------- | | **MCP Server** | Real-time doc search in Cursor, VS Code, Claude Code | | **Claude Skill** | Pre-loaded MeetStream context in Claude.ai chat | With both enabled, LLMs can answer questions about any MeetStream endpoint, generate working API calls, debug webhook payloads, and help architect integrations all grounded in accurate, up-to-date documentation.