For AI agents: a documentation index is available at the root level at /llms.txt and /llms-full.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
Contact Support
GuidesAPI ReferenceBuild with AI
GuidesAPI ReferenceBuild with AI
  • Introduction
    • Meetstream AI Overview
    • Meeting Platforms Supported
  • Get Started
    • Dashboard Setup
    • Create your First Bot
    • Idempotency Keys
  • App Integrations
    • Zoom Marketplace App Setup
    • Zoom App Production Submission
    • Zoom OBF Implementation
    • Google Signed-In Bots
  • Calendar Integrations
    • Google Calendar OAuth Setup
    • Outlook Calendar Setup
  • Transcription & Recordings
    • Create Bot with Post Call Transcription
    • Create Bot with Live Transcription
    • Create Bot with Per Participant Video
    • Create Bot with Per Participant Audio
  • Webhooks
    • Webhooks and Events
    • Set Up Local Server for Webhook
  • MIA (Meetstream Infrastructure Agents)
    • Create MIA
    • MIA Configurations
  • WebSockets
    • Real-time Audio Streaming
    • Real-time Video Streaming
    • Meeting Control and Command Patterns
    • Bridge Server Architecture
LogoLogo
Contact Support
On this page
  • 1) Why idempotency matters
  • 2) How it works (one-paragraph summary)
  • 3) Quick example
  • First call — creates the bot
  • Retry with the same key — replays the original
  • 4) The header
  • 5) Response codes
  • 507 replay response shape
  • 6) Rules and guarantees
  • What MeetStream guarantees
  • What MeetStream does not guarantee
  • 7) Best practices
  • DO
  • DON’T
  • 8) Reference implementations
  • Python (with requests and tenacity)
  • Node.js (with axios and axios-retry)
  • Worker queue pattern (recommended)
  • 9) FAQ
  • 10) Related docs
Get Started

Idempotency Key Guide for Bot Creation in MeetStream

||View as Markdown|
Was this page helpful?
Previous

Create your First Bot

Next

Zoom Bot Implementation Guide

Built with

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

$curl -X POST "https://api.meetstream.ai/api/v1/bots/create_bot" \
> -H "Authorization: Token <YOUR_API_KEY>" \
> -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):

1{
2 "bot_id": "bot_a1b2c3d4...",
3 "transcript_id": "t_111...",
4 "meeting_url": "https://meet.google.com/abc-defg-hij",
5 "status": "Active"
6}

Retry with the same key — replays the original

$curl -X POST "https://api.meetstream.ai/api/v1/bots/create_bot" \
> -H "Authorization: Token <YOUR_API_KEY>" \
> -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):

1{
2 "bot_id": "bot_a1b2c3d4...",
3 "transcript_id": null,
4 "meeting_url": "https://meet.google.com/abc-defg-hij",
5 "status": "Active"
6}

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

HeaderRequiredFormatDescription
Idempotency-KeyNoAny 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

StatusWhenMeaning
201 CreatedFirst call for a (user, key) pair, or any call without the headerBot was created. Standard create-bot response body.
507Subsequent call with a key that was already bound to a botReplay of the original bot. Treat this as a success. No new bot was created.
400 / 403 / 429 / 500Validation, wallet, or platform errorsStandard 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

1{
2 "bot_id": "<BotID of the originally created bot>",
3 "transcript_id": null,
4 "meeting_url": "<MeetingLink of the originally created bot>",
5 "status": "<current Status of the bot at lookup time>"
6}

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)

1import uuid
2import requests
3from tenacity import retry, stop_after_attempt, wait_exponential
4
5API_URL = "https://api.meetstream.ai/api/v1/bots/create_bot"
6API_KEY = "<YOUR_API_KEY>"
7
8
9def create_bot(meeting_link: str, video_required: bool = False) -> dict:
10 idempotency_key = str(uuid.uuid4())
11 headers = {
12 "Authorization": f"Token {API_KEY}",
13 "Content-Type": "application/json",
14 "Idempotency-Key": idempotency_key,
15 }
16 payload = {
17 "meeting_link": meeting_link,
18 "video_required": video_required,
19 }
20 return _send_with_retries(headers, payload)
21
22
23@retry(stop=stop_after_attempt(5), wait=wait_exponential(min=1, max=30))
24def _send_with_retries(headers: dict, payload: dict) -> dict:
25 response = requests.post(API_URL, json=payload, headers=headers, timeout=10)
26
27 if response.status_code in (201, 507):
28 return response.json()
29
30 if response.status_code >= 500:
31 response.raise_for_status()
32
33 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)

1import axios from "axios";
2import axiosRetry from "axios-retry";
3import { randomUUID } from "crypto";
4
5const API_URL = "https://api.meetstream.ai/api/v1/bots/create_bot";
6const API_KEY = "<YOUR_API_KEY>";
7
8const client = axios.create({ timeout: 10_000 });
9
10axiosRetry(client, {
11 retries: 5,
12 retryDelay: axiosRetry.exponentialDelay,
13 retryCondition: (err) => err.response?.status >= 500 || !err.response,
14});
15
16export async function createBot(meetingLink, videoRequired = false) {
17 const idempotencyKey = randomUUID();
18
19 const response = await client.post(
20 API_URL,
21 { meeting_link: meetingLink, video_required: videoRequired },
22 {
23 headers: {
24 Authorization: `Token ${API_KEY}`,
25 "Content-Type": "application/json",
26 "Idempotency-Key": idempotencyKey,
27 },
28 validateStatus: (status) => status === 201 || status === 507,
29 }
30 );
31
32 return response.data;
33}

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:

1def enqueue_bot_creation(meeting_link: str, user_event_id: str):
2 job = {
3 "meeting_link": meeting_link,
4 "user_event_id": user_event_id,
5 "idempotency_key": str(uuid.uuid4()),
6 }
7 sqs.send_message(QueueUrl=QUEUE_URL, MessageBody=json.dumps(job))
8
9
10def worker_handler(message: dict):
11 job = json.loads(message["Body"])
12 response = requests.post(
13 API_URL,
14 json={"meeting_link": job["meeting_link"]},
15 headers={
16 "Authorization": f"Token {API_KEY}",
17 "Idempotency-Key": job["idempotency_key"],
18 },
19 )
20 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 — full list of body fields accepted by POST /api/v1/bots/create_bot.
  • First Bot Quickstart — end-to-end walkthrough of creating your first bot.
  • Bot Lifecycle Webhook Events — webhook events you’ll receive after the bot is created.
  • API reference: https://docs.meetstream.ai/api-reference/api-reference/bot-endpoints/create-bot