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
| Lambda | Trigger | Purpose |
|---|---|---|
processWebhookRequest (src/app.ts) | ANY / on a REST API Gateway | Receives webhooks from the meeting-bot vendor (see below) |
processCalendarWebhookRequest (src/calendarWebhook.ts) | POST / on an HTTP API Gateway | Receives 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 path — domainId 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
helixrepo 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):
- Create a Google Workspace account for the agent address.
- Register a GCP project/OAuth client for it.
- Create a Recall "Application Key", then call Recall's Calendar API (
POST /api/v1/calendar/authenticate/) with the agent'suser_idto get a calendar auth token. - 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. PUT /api/v1/calendar/user/sets bot preferences:bot_name: "Helix",record_non_host/recurring/external/internal: true,record_confirmed/only_host: false.- (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.jsimpersonates the mailbox via a GCP service account JWT (subject: helixEmail, scopegmail.readonly) — configured byapi/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 fromapi/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 viaapi/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/autoJoinsetsuser_calendar_account.is_auto_join_enabledandauto_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: branchtesting/staging/production→ matching environment;semantic-releaseonly runs offproduction). - Infra is defined entirely in
template.yml(SAM/CloudFormation) — SQS queues, DynamoDB table, API Gateways, IAM, and per-environment env vars (includingRECALL_CALENDAR_USERabove). - 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.ymlonly 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.