Skip to content

feat: Calling support for both incoming and outgoing. #194

Merged
shridarpatil merged 15 commits intomainfrom
calling
Feb 25, 2026
Merged

feat: Calling support for both incoming and outgoing. #194
shridarpatil merged 15 commits intomainfrom
calling

Conversation

@shridarpatil
Copy link
Owner

  1. Handle incoming and outgoing calls.
  2. IVR support for incoming calls.
  3. Record calls and store them on S3.
  4. TURN server support for UDP blocked firewall.

…ing calls

Implements the full WhatsApp Business Calling API integration:
- WebRTC-based voice calling with Opus codec and DTMF support
- IVR flow engine with configurable menus and text-to-speech
- Incoming call handling with pre-accept pattern per Meta's recommendation
- Outgoing calls (agent → consumer) with dual PeerConnection bridge
- Call transfers between IVR and live agents with hold music
- Call permission request flow (72h validity per WhatsApp rules)
- Frontend: call logs, IVR flow editor, call transfers, active call panel
- CallButton integrated in chat header for agent-initiated calls
…ovements

- Add Piper TTS integration for IVR greeting generation (text → OGG/Opus)
- Fix outgoing calls to use contact_id instead of masked phone number
- Fix WhatsApp API payloads per official Meta docs (InitiateCall, SendCallPermissionRequest)
- Add call permission check via WhatsApp API before initiating outgoing calls
- Handle business-initiated call status webhooks (RINGING/ACCEPTED/REJECTED)
- Handle orphaned outgoing call terminate webhooks after session cleanup
- Add call_permission_reply webhook processing
- Fix OGG/Opus audio playback in AudioPlayer using pion oggreader
- Add WebRTC UDP port range configuration (default 10000-10100)
- Add TTS setup documentation to README
Replace hardcoded Google STUN server with config-driven ICE servers.
Adds GET /api/calls/ice-servers endpoint so the frontend fetches
server config from backend instead of hardcoding.
…e DTMF detection

- Add relay_only config to force all media through TURN server
- Add public_ip config for NAT 1:1 IP mapping on cloud instances
- Fix OGG/Opus audio player to properly split multi-packet pages using
  segment table (was sending entire pages as single RTP packets)
- Detect inline DTMF (telephone-event) on same m-line as audio track,
  since WhatsApp sends both codecs on a single track
- Fix nil UUID panic in transfer WebSocket broadcast
- Add 500ms stabilization delay after WebRTC connection before IVR
- Deduplicate waiting transfers by checking ID before pushing
- Handle call_ended event to clean up active agent call state
- Add onconnectionstatechange to peer connections to stop timer
  when WebRTC disconnects/fails (e.g. caller hangs up)
EndTransfer was only closing the server-side WebRTC connections without
notifying WhatsApp, leaving the caller's phone still connected.
- Atomically claim transfer in DB (UPDATE WHERE status=waiting) so only
  one agent can accept; revert to waiting if WebRTC setup fails
- Claim transfer in-memory under session mutex before releasing lock
- Cap frontend ICE gathering at 3s instead of waiting indefinitely
- Reduce server-side ICE gathering timeout from 15s to 5s
…improvements

- Add OGG/Opus call recorder that captures both directions during bridge
- Add S3 storage client for uploading recordings and generating presigned URLs
- Add recording playback in call log detail dialog with mic icon indicator
- Split IVR flow is_active into is_active (enabled/disabled) and is_call_start
  (entry point for incoming calls), with unique partial index enforcement
- Add call direction and IVR flow filters to call logs
- Apply phone number masking to call log endpoints
- Fix missed call detection for pre-accepted incoming and unanswered outgoing calls
- Block goto_flow to disabled IVR flows
…sion

- Migrate CallLogsView and CallTransfersView to shared DataTable component
  with server-side pagination for consistent UI across all views
- Add maxHeight prop to DataTable for scrollable table with fixed pagination
- Add border-top and padding to DataTable pagination controls
- Scope call log endpoints by permission: users without call_logs:read
  see only their own calls (agent_id filter), admins/managers see all
- Remove call_logs:read from default agent role
… flows

- Fix calling sidebar submenu collapsing by using /calling as parent path
- Add phone number search to call logs with debounced input
- Add consistent padding to calling view table cards
- Accumulate IVR path across goto_flow transitions instead of overwriting
…alling bugs

- Include digit and label in goto_flow IVR path marker so the tree shows
  which key was pressed to trigger a flow transition
- Add IVRPathTree component with proper nested tree rendering (border-l
  indentation, recursive depth) replacing the flat badge list
- Reuse IVR AudioPlayer across goto_flow transitions to maintain RTP
  sequence continuity (prevents packet drops from seq reset)
- Fix toggle active/call-start wiping IVR menu by making UpdateIVRFlow
  only include non-zero fields in the update map
- Validate TTS configuration and surface TTS errors to UI instead of
  silently logging them
- Set agent_id on CallLog during transfer acceptance so ended webhook
  doesn't incorrectly mark transferred calls as missed
- Re-read CallLog before deciding final status to catch late agent_id writes
@shridarpatil shridarpatil changed the title feat: Calling support fot both incoming and cout going. feat: Calling support for both incoming and cout going. Feb 25, 2026
@shridarpatil shridarpatil changed the title feat: Calling support for both incoming and cout going. feat: Calling support for both incoming and out going. Feb 25, 2026
Replace fixed waitForTimeout delays with proper element-based waits in
template sending tests. Add waitForTemplatesLoaded() helper that waits
for the spinner to disappear and template items to render.

Fix all golangci-lint errcheck violations by handling Close/Remove
return values. Apply De Morgan's law for staticcheck QF1001 and use
tagged switch for QF1003.
Create a dedicated contact per test run instead of reusing contacts[0]
which may have messages from a different WhatsApp account. When existing
messages set selectedAccount to a mismatched account, the TemplatePicker
filters by that account and returns 0 results.
Add comprehensive calling documentation covering IVR flows, call
transfers, recording, WebRTC configuration, TTS setup, and network
requirements. Move the TTS section from README to the docs site.
Add calling to the README features list and docs sidebar.
Switch Docker base from Alpine to Debian (glibc required by Piper),
download Piper binary + default English voice model during build,
and install espeak-ng and opus-tools as runtime dependencies.
@shridarpatil shridarpatil changed the title feat: Calling support for both incoming and out going. feat: Calling support for both incoming and outgoing. Feb 25, 2026
@shridarpatil shridarpatil merged commit 7e0fc70 into main Feb 25, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant