Skip to main content

Meeting bots & the helix repo

The standalone helix repo is a small AWS Lambda service (AWS SAM, TypeScript, ESM). Its entire job is to get a bot into a meeting via a third-party vendor and forward whatever comes back to Hub over SQS — a deliberately thin, stateless design. See Why so thin? below.

For the full local dev / SAM / testing workflow, see the repo's own README.md and SETUP.md rather than duplicating it here — this page focuses on what a Hub developer needs to know about how it fits together.

Two Lambda functions, two API Gateways

LambdaTriggerPurpose
processWebhookRequest (src/app.ts)ANY / on a REST API GatewayReceives webhooks from the meeting-bot vendor (see below)
processCalendarWebhookRequest (src/calendarWebhook.ts)POST / on an HTTP API GatewayReceives Google/Microsoft calendar push notifications, forwards them raw to Hub — see Architecture → calendar webhook flow

Meeting BaaS (current) and Recall.ai (kept as a fallback)

Meeting BaaS is the active vendor. Hub schedules bots via api/utils/meetingBaas.js (POST https://api.meetingbaas.com/v2/bots, bot_name: 'Helix', recording_mode: 'audio_only', transcription via provider gladia), and the helix repo's handleMeetingBaasRequest (src/app.ts) handles its webhook callback: it validates an x-mb-secret header against an SSM-stored secret, downloads the transcription JSON, and maps participants/speakers/attendees into the outbound SQS payload. Domain resolution is a known gap on this pathdomainId currently always resolves to undefined on the Meeting BaaS branch.

The switch from Recall.ai to Meeting BaaS was driven by cost — the Recall.ai integration is deliberately kept in place in both repos as a fallback in case of a switch back. src/app.ts still handles Recall's webhook shape (status.code / data.code), fetches the bot's transcript/participants/calendar id via src/lib/recallStore.ts, and looks up a legacy domainId from a DynamoDB DomainsTable keyed by the calendar's ical_uid (explicitly marked in code as being phased out independent of the vendor question). Full account-provisioning steps for Recall are in the repo's SETUP.md.

A related fossil: constants/changelog.js in Hub still has SOURCE_RECALL = 'recall' as the changelog "source" for bot-created meetings (vs. SOURCE_USER for manual reprocessing) — a naming holdover from when Recall.ai was the default vendor.

The helix repo forwards calendar attendees and live meeting participants raw and unmatched; Hub resolves diarized speakers to emails and does all the matching in one place. This is the current design intent: helix stays pure bot-orchestration/webhook-ingestion; all matching, scoring, and storage live in Hub.

Why so thin?

Keeping OpenAI calls, prompts, skill definitions, and scoring entirely in Hub means:

  • a single place to change a prompt or skill weight
  • Hub's existing auth/DB/permissions apply directly to Helix data, since it lives in the same database
  • the helix repo can be redeployed/rolled back independently of scoring logic changes

Agent account & calendar integration

Helix "attends" a meeting because a real mailbox is invited to it as a calendar guest — the agent account. Per environment (from the helix repo's template.yml, RECALL_CALENDAR_USER, switched by an IsProductionEnvironment condition):

  • dev: helix-testing@5app.com
  • staging: helix-staging@5app.com
  • prod: helix@5app.com

Provisioning this account (summarised from the helix repo's SETUP.md — see it directly for the exact steps/URLs, no secrets reproduced here):

  1. Create a Google Workspace account for the agent address.
  2. Register a GCP project/OAuth client for it.
  3. Create a Recall "Application Key", then call Recall's Calendar API (POST /api/v1/calendar/authenticate/) with the agent's user_id to get a calendar auth token.
  4. Complete a browser OAuth consent flow signed in as the agent account, granting calendar.events.readonly + userinfo.email, redirecting through Recall's own OAuth callback.
  5. PUT /api/v1/calendar/user/ sets bot preferences: bot_name: "Helix", record_non_host/recurring/external/internal: true, record_confirmed/only_host: false.
  6. (Optional) Configure AWS Transcribe as an alternative transcription provider for Recall (~$0.024/min).

At runtime, getRecallMeeting (src/lib/recallStore.ts) re-authenticates per request against Recall's calendar API using this account to fetch meeting data.

Gmail watch on the agent inbox

Separately from the helix repo, Hub itself watches the agent account's Gmail inbox directly:

  • api/modules/userCalendar/gmailWatchHelixEmail.js impersonates the mailbox via a GCP service account JWT (subject: helixEmail, scope gmail.readonly) — configured by api/config/helix.js (HELIX_GCP_SERVICE_ACCOUNT, HELIX_GCP_SERVICE_ACCOUNT_PRIVATE_KEY, HELIX_EMAIL, HELIX_MEETING_INVITE_EMAIL_LABEL_NAME, HELIX_GCP_PUB_SUB_TOPIC_NAME).
  • POST /webhook/helix-email-watcher (api/routes/watchHelixEmailInbox/helixEmailWatcherWebhook.js, mounted from api/routes/webhook.js) is a Gmail Pub/Sub push endpoint. It verifies the request came from Google (OAuth2Client.verifyIdToken, checked against the configured service account email), then for any newly-labelled invite email extracts a Teams/Zoom/Google Meet link from the body and triggers a bot join via api/utils/meetingBaas.js.
  • This is how Helix can join a meeting even when the user never explicitly connected their own calendar to Hub — as long as the agent account was invited directly.

User-side calendar connection

Independently, a user can connect their own calendar to Hub (Google/Microsoft OAuth callbacks under me/calendar/...), enabling:

  • calendarSync.js / calendarSubscriptionRunner.js — periodic sync of the user's own upcoming meetings.
  • Auto-join settings: PATCH /me/calendar/autoJoin sets user_calendar_account.is_auto_join_enabled and auto_join_settings (meeting_type: 'all' | 'internal' | 'external', only_meetings_I_organise).
  • api/utils/meetingBaas.js's bot lifecycle functions (createBots, updateScheduledBot, deleteScheduledBot, listBots, checkUserHasUpcomingMeetingBooked, hasBotChanged) use these settings to decide whether/when to schedule a bot for a given meeting.

Environments & deployment (helix repo)

  • Three environments — testing, staging, production — each with its own SAM deploy parameters (samconfig.toml) and CircleCI branch-triggered deploy (.circleci/config.yml: branch testing/staging/production → matching environment; semantic-release only runs off production).
  • Infra is defined entirely in template.yml (SAM/CloudFormation) — SQS queues, DynamoDB table, API Gateways, IAM, and per-environment env vars (including RECALL_CALENDAR_USER above).
  • Secrets (Recall API token, Meeting BaaS API key/callback secret) are fetched from AWS SSM Parameter Store at Lambda cold start.
  • No Dockerfile for the Lambda itself — docker-compose.yml only runs LocalStack for local SQS/SNS/SSM emulation during tests (npm run spec).

To re-analyse a meeting without going back through a bot, see Scoring engine → Manual reprocessing.