HarborFM

Group Calls (WebRTC)

Group calls let you record remote guests via WebRTC. The host starts a call; guests join by link or code. Recordings can be added as segments in the episode editor.

You can also schedule a meeting ahead of time: HarborFM reserves a join link and code, you invite guests, then start the meeting when you are ready. Ad-hoc Start Group Call stays available separately.

Note: Group calls require the optional webrtc-service and correct configuration. If you don't see the Record or group-call UI, see the main README section "WebRTC (group calls)" and Deployment: Docker.

Scheduled Meetings

Schedule a group call for a future start time from the episode editor. HarborFM reserves a join link and 4-digit code (and dial-in details when phone dial-in is enabled) so guests can prepare before you go live.

Limits

  • One scheduled meeting per episode at a time.
  • Up to 50 active scheduled meetings per account (across all shows).
  • Schedule or reschedule at most 1 year ahead.
  • Guests can use the link or code from 1 hour before the start until 4 hours after. Outside that window the join page shows a clear status (too early or expired).

Schedule and Manage

  1. Open the episode editor. In the summary card, use Schedule (or the meeting panel when WebRTC is enabled).
  2. Pick a start time and save. HarborFM emails you a confirmation if your account has an email address.
  3. From Manage, you can:
    • Copy the join code and dial-in number (when enabled)
    • Copy a share link (optional guest name for join prefill)
    • Send email invites (name + email)
    • See invited guests and remove invites
    • Update the start time or cancel the meeting
  4. When the join window is open, use Start Meeting on the episode summary. That starts the reserved meeting (same code and link). Start Group Call remains a separate ad-hoc call with its own code.

Invite creates are rate limited (default 5 per 5 minutes per user) so share links and emails cannot be spammed.

Guest Experience

  • Too early - The join page says the meeting opens 1 hour before the scheduled start.
  • Waiting for host - Inside the window, before you start the meeting, guests see that the host has not started yet. The page refreshes join status about every 30 seconds.
  • Live - After you start the meeting, guests join the call as usual.
  • Expired / ended / cancelled - The join page explains that the meeting is no longer available.

Emails and Calendar

  • Creator confirmation on create (if the creator account has email).
  • Guest invite emails with the join link, code, dial-in when enabled, an ICS calendar attachment, and a Google Calendar link. Reply-To and the calendar organizer use the host email when available. Local times use a cast member’s private time zone when the invite is tied to cast; otherwise the host zone or UTC.
  • 4-hour reminder emails go to emailed invitees and to the host who scheduled the call.
  • Reschedule and cancel emails go to guests who were invited by email (not link-only shares).
  • When the episode is published for the first time, emailed invitees get a one-time notice (not the host). Title or description edits do not send meeting emails.
  • Guest episode review - When the episode first becomes scheduled (or published and unlisted), HarborFM emails preview links to the meeting host, emailed invitees, and episode cast with an email. See Episodes: Guest episode review.
  • Invite and reminder emails can report open status when Email Event Tracking is on in Settings (default on). Schedule Meeting shows open status per invitee.

See Also

Phone Dial-In

HarborFM supports Zoom-style phone dial-in via Telnyx (DID + IVR + bidirectional media) into the same mediasoup room as browser guests. Each phone caller is its own producer so multitrack recording stays intact.

Live Telnyx (PSTN)

  1. In Settings → WebRTC: enable dial-in, set the phone number, consent prompt, Telnyx API key, and Call Control connection ID.
  2. Point the Telnyx Call Control App webhook to https://your-host/api/call/dial-in/webhook.
  3. Set WebRTC public WebSocket URL to a URL Telnyx can reach (e.g. wss://your-host/webrtc-ws). Production nginx/Caddy already proxies /webrtc-ws/. For local tunnels through Vite, /webrtc-ws is proxied to the webrtc service.
  4. Start a group call, dial the DID, enter the 4-digit join code. The phone appears on the roster; audio bridges both ways (no FakeDialIn tone).

Requires WEBRTC_SERVICE_SECRET (used to mint media WebSocket tokens).

Optional: set DIAL_IN_IVR_NAME on the main app to override the spoken welcome brand (default HarborFM Podcasting), e.g. Welcome to Acme Podcasts. Please enter the four-digit call code.

HD Voice: In Settings → WebRTC, leave Prefer HD Voice on so HarborFM asks Telnyx for L16 @ 16 kHz on the media bridge. Also put G722 first in the Telnyx Connection codec list for the phone↔Telnyx leg. Callers still need carrier HD Voice / VoLTE; AirPods alone do not enable wideband.

Fakedialin (Development / E2e)

Without a Telnyx API key, or for automated tests, HarborFM can inject fake phone callers when DIAL_IN_FAKE=1 on both the main app and webrtc-service.

  1. Start a group call and note the 4-digit join code.
  2. POST /api/call/dial-in/fake/join with { "joinCode": "1234", "displayName": "Phone Guest" }.
  3. The caller appears on the roster with source: "phone" and is recorded as its own multitrack segment when you Record.

Leave with POST /api/call/dial-in/fake/leave (participantId / sessionId). Fake IVR webhooks also work against POST /api/call/dial-in/webhook when DIAL_IN_FAKE=1.

Starting and Joining Calls

Group calls use WebRTC so you can record remote guests in real time. The host starts a call from the episode editor; guests join via a link or a short code. For a reserved time and invites, see Scheduled meetings.

Starting a Call (Host)

  1. Open the episode editor for the episode you want to record.
  2. Use Start Group Call for an ad-hoc call, or Start Meeting if you already scheduled one and the join window is open. The app may ask for microphone (and optionally camera) permission.
  3. Once the call room is created, you'll see a join link and often a 4-digit code. For ad-hoc calls, share those with guests. For scheduled meetings, guests already have the reserved link and code from invites or Manage.
  4. When guests have joined, you can start recording from the in-call controls. See Call settings and recordings.

Guest join links use the podcast linking or managed domain when configured (e.g. https://your-show.example/call/join/...) instead of the app hostname. Join pages serve episode/podcast Open Graph meta for link previews.

If you don't see the group-call option, WebRTC may not be enabled or configured on the instance. See the main README ("WebRTC (group calls)") and ensure WEBRTC_ENABLED, WEBRTC_SERVICE_URL, and WEBRTC_PUBLIC_WS_URL are set. After Hostname is set in Settings, WebRTC Settings values take precedence over WEBRTC_* env vars (env still seeds empty settings). Changing Hostname updates the public WebSocket URL when it still pointed at the previous host.

Joining a Call (Guest)

  1. Open the join link in your browser (or enter the 4-digit code on the call-join page if the host shared it).
  2. Allow microphone (and camera if requested) when the browser asks.
  3. You'll enter the same room as the host. Wait for the host to start recording when ready.

Guests see a recording banner with elapsed time above "You're In The Call" while recording is active. Keep the tab active when possible; on mobile, returning to the tab remounts the mediasoup room if the producer died while backgrounded so you can produce again.

Troubleshooting

  • No audio / can't connect - The server must have UDP ports open for WebRTC (default range 40000–40200, or 41000–41100 in Docker). Behind NAT, the admin must set MEDIASOUP_ANNOUNCED_IP to the server's public IP. See main README and Deployment.
  • No "Record" or group-call UI - Confirm WEBRTC_ENABLED and the WebRTC URLs are set; check server and webrtc-service logs (docker compose logs webrtc or pm2 logs webrtc).
  • Guest drops from participant list after ~10 minutes - Guests send a signaling WebSocket heartbeat every 30s (same as the host) so reverse proxies do not idle-drop /api/call/ws. Ensure your proxy allows long-lived WebSockets.
  • Silent after switching apps on mobile - Returning to the tab should recreate the producer; if audio stays silent, leave and rejoin or have the host restart the call.

See Also

Call Settings and Recordings

During a group call you can use in-call controls, chat, and soundboard, and save the call as a recording that becomes a segment.

In-Call Controls

  • Mute / unmute - Toggle your microphone.
  • Leave - Leave the call (host may have additional options to end the call for everyone).
  • Settings - Mic selector, auto gain control, listen-to-self, volume, and related options.
  • Chat - In-call group chat.
  • Soundboard - Short audio clips with waveform preview and search; play into the call.
  • Wake lock - On mobile, the join page tries to keep the screen awake during calls.

Show Notes for Guests

Show Notes are shared with guests by default. Participants can open See Show Notes to view planned topics and durations (switch the episode panel to Host only if you want them private). Notes update live over the call WebSocket. See Episodes: Show Notes.

Recording the Call

  1. As the host, start the call and wait for guests to join (see Starting and joining calls).
  2. Click Record (or "Start recording") in the call UI. All participants' audio is captured (multitrack; guest remounts create a new producer so segments stay time-aligned).
  3. When finished, click Stop recording. The webrtc-service writes the recording to disk; the main app then makes it available.
  4. The recording can be added as a segment to the current episode. Follow the prompt or use the episode editor to attach the recording; it will appear in the segment list like any other segment.

You can then trim, reorder, or combine it with other segments and run Build Final Episode. See Segments.

Host-Away Behavior

If the host leaves or disconnects, the call may stay open for a short grace period (configurable by the instance; page refreshes use a longer remount grace so the call is less likely to end). If the host is away too long, the call may end. Recording is typically tied to the host; if the host leaves during recording, behavior may vary (e.g. recording stops). See main README for HOST_AWAY_GRACE_* and HOST_AWAY_CHECK_INTERVAL_MS.

See Also