> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tryzuko.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Meeting Bot

Zuko includes a standalone **meeting-bot** service (`apps/meeting-bot`) that automatically joins Google Meet meetings, records audio/video, and streams live transcripts back to the backend.

## What it does

* Joins a Google Meet URL using headless browser automation (Puppeteer)
* Records the meeting as a WebM file and uploads it to S3
* Streams live transcripts to the backend in real-time using Deepgram
* Captures chat messages and responds to `@zuko` mentions via the AI agent
* Emits status webhooks throughout the meeting lifecycle
* Auto-leaves when it's the only participant remaining

***

## How it works

When the backend calls `POST /api/google-meet/join`, the meeting-bot:

1. Launches an ephemeral [Fly.io](https://fly.io) machine (in production) or runs locally (in dev)
2. Opens Chrome via Puppeteer and navigates to the Meet URL
3. Injects a browser script that extracts participants and chat via a local WebSocket
4. Streams audio through FFmpeg to Deepgram for live transcription
5. Sends transcript chunks (buffered at \~600 chars) to the backend
6. On meeting end, uploads the recording and full transcript JSON to S3
7. Emits a `completed` webhook with the S3 artifact keys

***

## Environment variables

`apps/meeting-bot` reads:

* `BACKEND_URL` — backend base URL (e.g. `http://localhost:3001`)
* `AGENT_TOKEN` — shared token for authenticating requests to the backend
* `DEEPGRAM_API_KEY` — Deepgram API key for live transcription
* `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`, `AWS_BUCKET_NAME` — S3 credentials for uploading recordings and transcripts
* `FLY_API_TOKEN`, `FLY_APP_NAME` — Fly.io credentials for launching worker machines (production only)
* `PUPPETEER_EXECUTABLE_PATH` — path to the Chrome binary
* `BOT_NAME` — display name shown in the meeting (default: `Zuko AI`)
* `PORT` — HTTP server port (default: `8000`)

The backend must have the same `AGENT_TOKEN` configured to validate incoming transcript and callback requests.

***

## Run locally

```bash theme={null}
npx nx run @zuko/meeting-bot:dev
```

***

## API

### Join a meeting

**POST** `/api/google-meet/join`

Triggers the bot to join a Google Meet.

```json theme={null}
{
  "meetingId": "clx1234abcd",
  "meetingUrl": "https://meet.google.com/abc-defg-hij",
  "callbackUrl": "https://your-backend/api/meetings/webhook"
}
```

| Field         | Type   | Required | Description                    |
| ------------- | ------ | -------- | ------------------------------ |
| `meetingId`   | string | Yes      | Meeting ID from the database   |
| `meetingUrl`  | string | Yes      | Full Google Meet URL           |
| `callbackUrl` | string | No       | Webhook URL for status updates |

**Response**

```json theme={null}
{ "message": "Meeting scheduled" }
```

***

## Status webhooks

The bot sends `POST` requests to `callbackUrl` (or `BACKEND_URL/api/meetings/webhook`) at each lifecycle stage.

```json theme={null}
{
  "meetingId": "clx1234abcd",
  "event": "completed",
  "data": {
    "recording": "meetings/clx1234abcd/recording.webm",
    "transcript": "meetings/clx1234abcd/transcript.json"
  }
}
```

| Event                  | Description                                          |
| ---------------------- | ---------------------------------------------------- |
| `fly-machine-launched` | Fly worker machine created                           |
| `in_progress`          | Bot successfully joined the meeting                  |
| `processing`           | Meeting ended; uploading artifacts to S3             |
| `completed`            | Recording and transcript uploaded; S3 keys in `data` |
| `failed`               | An error occurred                                    |
| `rejected`             | Host denied the bot entry                            |

***

## Transcript streaming

During the meeting, the bot streams transcript chunks to the backend in real-time.

**POST** `/api/meetings/:meetingId/transcript-chunks`

### Headers

* `x-agent-token` — must match `AGENT_TOKEN`
* `x-user-provider` — `google-meet`

### Payload

```json theme={null}
{
  "text": "Let's align on the Q2 roadmap...",
  "sequence": 12,
  "isFinal": true
}
```

Chunks are buffered at \~600 characters with 100-character overlap to preserve context across boundaries. On meeting end, any remaining buffered text is flushed.

***

## S3 artifacts

After a meeting, two files are written to S3:

| File       | Path                                   | Description                                                        |
| ---------- | -------------------------------------- | ------------------------------------------------------------------ |
| Recording  | `meetings/{meetingId}/recording.webm`  | Raw WebM video; the backend transcodes this to MP4 asynchronously  |
| Transcript | `meetings/{meetingId}/transcript.json` | Full speaker-diarized transcript with timestamps and chat messages |

***

## Chat and AI responses

The bot captures all chat messages from the meeting. When a participant sends a message containing `@zuko`, the bot queries the backend AI agent and posts the response back to the meeting chat.

***

## Deployment

In production, each meeting runs on its own ephemeral Fly.io machine (1 CPU, 2 GB RAM). The machine is launched when a join request arrives and automatically destroyed after cleanup completes.
