When you launch a bot with the Attendee API, you get back an ID and an initial state. But that's just the beginning of the journey. To build robust, responsive applications, you need to understand the full lifecycle of a bot—from the moment it's created until its final assets are ready. Tracking these state changes allows you to provide real-time feedback to your users, trigger data processing pipelines, and build a seamless product experience.
The best way to track this lifecycle is with webhooks. By subscribing to the bot.state_change
event, you can stop polling the API and let Attendee tell you exactly when something important happens.
The Bot Lifecycle Diagram
At a high level, a bot moves through a sequence of states. The diagram below illustrates the most common path, from the initial API call to the final `ended` state, along with key alternative paths.
A State-by-State Breakdown
The Entry Phase: ready
& joining
When you make a POST /bots
request, the bot is created in the ready
state and immediately transitions to joining
. This is the API call that kicks off the entire process.
ready
: The bot object has been created in our system, but no action has been taken yet. This state is momentary.joining
: The bot is actively attempting to connect to the meeting URL. This can take anywhere from a few seconds to a minute. In your UI, this is the perfect time to show a "Connecting bot..." or similar loading state.waiting_room
: An optional state. If the meeting has a waiting room enabled, the bot will enter this state until it is admitted by a host.
The Active Phase: joined_recording
Once successfully in the meeting and all systems are go, the bot enters joined_recording
. This is the primary active state where data is captured.
joined_recording
: The bot is in the meeting and actively capturing audio and/or video. If you've subscribed to thetranscript.update
webhook, you will start receiving transcript fragments during this phase.
The Finalization Phase: leaving
, post_processing
& ended
When the meeting ends (or you call the /leave
endpoint), the bot begins its shutdown sequence. This is a critical phase for data integrity.
leaving
: The bot has been instructed to leave and is disconnecting from the meeting platform.post_processing
: This is one of the most important states. The bot has left the meeting and is now processing the raw data. This includes finalizing the recording file, compiling the complete transcript, and uploading assets. You cannot download the final assets in this state. Your UI should indicate that processing is underway.ended
: Success! All processing is complete. The final recording and full transcript are now available for download from their respective API endpoints. This is the trigger to notify your user that their meeting summary is ready.
Handling Failures: fatal_error
Sometimes things go wrong. The fatal_error
state indicates that the bot encountered a problem it could not recover from and has terminated.
fatal_error
: Common causes include an invalid meeting link, the bot being denied entry or removed from the meeting, or an internal infrastructure issue. When you receive this state via webhook, check the bot'serror_message
field in the API or look for logs in the Attendee dashboard.
Putting It All Together with Webhooks
The lifecycle becomes powerful when you combine it with API calls. Here is a typical end-to-end flow for an application using Attendee:
- Your application calls
POST /api/v1/bots
with a meeting URL. You receive abot_id
and store it. - You update your UI to show a "Connecting..." status based on the initial
joining
state. - Your webhook endpoint listens for
bot.state_change
events. You can update your UI in real-time as the state changes tojoined_recording
. - Eventually, your webhook receives an event where
new_state
isended
. This is your cue to act. - Using the stored
bot_id
, you now make requests toGET /api/v1/bots/{bot_id}/transcript
and/orGET /api/v1/bots/{bot_id}/recording
to fetch the final assets. - You process the data and notify your user that their meeting content is ready.
Example Webhook Payload
When a state change occurs, Attendee sends a POST request to your webhook URL. The payload for a bot.state_change
event will look like this:
{
"idempotency_key": "evt_1a2b3c4d5e6f7g8h",
"bot_id": "bot_3hfP0PXEsNinIZmh",
"bot_metadata": { "user_id": "usr_abc123" },
"trigger": "bot.state_change",
"data": {
"new_state": "ended",
"old_state": "post_processing",
"created_at": "2023-07-15T14:30:45.123456Z",
"event_type": "post_processing_completed",
"event_sub_type": null
}
}
Example Webhook Handler
Here is a simple Python Flask server that listens for these webhooks and demonstrates how to build logic around the bot's lifecycle.
from flask import Flask, request, jsonify
import hmac
import hashlib
import json
import os
app = Flask(__name__)
# Store your webhook secret securely from an environment variable
WEBHOOK_SECRET = os.environ.get("ATTENDEE_WEBHOOK_SECRET")
def fetch_and_process_assets(bot_id):
"""Placeholder function to fetch assets after meeting ends."""
print(f"🚀 Kicking off asset processing for bot {bot_id}...")
# In a real app, you would make API calls here:
# 1. GET /api/v1/bots/{bot_id}/transcript
# 2. GET /api/v1/bots/{bot_id}/recording
# 3. Save to your database, summarize, etc.
# 4. Notify the user.
print(f"✅ Finished processing for bot {bot_id}.")
@app.route("/webhook", methods=["POST"])
def attendee_webhook():
# --- Webhook signature verification (important for security) ---
signature = request.headers.get("X-Webhook-Signature")
if not signature or not WEBHOOK_SECRET:
return "Missing signature or secret", 400
payload = request.get_data()
expected_signature = hmac.new(
WEBHOOK_SECRET.encode(), payload, hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected_signature, signature):
return "Invalid signature", 400
# --- Handle the event payload ---
event = json.loads(payload)
trigger = event.get("trigger")
bot_id = event.get("bot_id")
if trigger == "bot.state_change":
new_state = event["data"]["new_state"]
print(f"Bot {bot_id} changed state to: {new_state}")
# Example application logic based on state
if new_state == "joined_recording":
# Your UI can now show "Recording in progress..."
pass
elif new_state == "post_processing":
# Your UI can now show "Finalizing recording..."
pass
elif new_state == "ended":
# Final assets are ready! Trigger your processing pipeline.
fetch_and_process_assets(bot_id)
elif new_state == "fatal_error":
# Log the error and notify the user of the failure.
print(f"❌ Bot {bot_id} encountered a fatal error. Check dashboard for details.")
return "Webhook received", 200
if __name__ == "__main__":
app.run(port=5005)
Master Your Integration
Understanding the bot lifecycle and using webhooks is the key to creating a seamless user experience. Dive into our documentation for more details on webhook payloads and API endpoints.
View Webhooks Documentation