DEEP DIVES & TECHNICAL GUIDES • APRIL 05, 2024

The Bot Lifecycle: From 'Joining' to 'Ended'

A bot's state is crucial for application logic. This guide breaks down what joining, post_processing, and fatal_error mean for your integration.

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.

ready
API Call: POST /bots
joining
Enters Meeting
joined_not_recording
Optional Path
waiting_room
Recording Starts
joined_recording
Meeting Finishes
leaving
post_processing
Assets Ready
ended
fatal_error

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.

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.

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.

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.

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:

  1. Your application calls POST /api/v1/bots with a meeting URL. You receive a bot_id and store it.
  2. You update your UI to show a "Connecting..." status based on the initial joining state.
  3. Your webhook endpoint listens for bot.state_change events. You can update your UI in real-time as the state changes to joined_recording.
  4. Eventually, your webhook receives an event where new_state is ended. This is your cue to act.
  5. Using the stored bot_id, you now make requests to GET /api/v1/bots/{bot_id}/transcript and/or GET /api/v1/bots/{bot_id}/recording to fetch the final assets.
  6. 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:

Webhook Payload: bot.state_change
{
  "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.

Python Webhook Handler (app.py)
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