Skip to content

feat(conv): choose which voice channel answers a chat widget's calls - #1151

Merged
kmonsoe merged 1 commit into
mainfrom
feat/widget-voice-channel-link
Oct 7, 2026
Merged

kmonsoe merged 1 commit into
mainfrom
feat/widget-voice-channel-link

Conversation

@kmonsoe

@kmonsoe kmonsoe commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Why

When an organization has more than one voice channel, the chat widget's call button only works if the widget channel names one in voiceChannelId. Otherwise the server returns multiple_voice_channels_without_widget_routing instead of guessing. But nothing could set that field: no MCP tool, no /v1 route and no dashboard control. On top of that, conv_update_widget_channel rebuilt the stored config from a fixed list of fields, so any update, even one that only changed the origin allow-list, silently erased a link someone had set by hand.

What changes

Backend. Both MCP tools and the /v1 routes call WidgetChannelAdminService, so the change lands in one place.

  • conv_create_widget_channel / POST /v1/conversations/channels/widget take an optional voiceChannelId.
  • conv_update_widget_channel / PATCH /v1/conversations/channels/widget/:id take a nullable voiceChannelId: omit it to keep the link, pass null to unlink.
  • The id is checked before anything is written. It must be a Vapi or Threll voice channel in the same organization that hasn't been deleted; a deactivated one is accepted because it can be reactivated. Anything else gets a 400 conv_widget_voice_channel_invalid with a code, so the dashboard shows it translated.
  • Updates now start from the stored config, so fields the update doesn't mention are kept.
  • The vendor list (vapi, threll) is one constant shared by the call-time lookup and the new check.

Dashboard (channels.tsx)

  • Widget cards get an Edit dialog for the origin allow-list and the voice channel. The allow-list is only sent when it changed, so an older widget without one can still change its voice channel without tripping the allow-list requirement.
  • The create dialog has the same voice channel picker. "Automatic" shows the channel's name when there is exactly one active voice channel. Deactivated channels are labelled, and a link to a deleted channel shows as "Removed channel".
  • Each widget card says where its calls go, and gets the amber stripe when calls are off: several active voice channels with none picked, a deleted link, or a deactivated one. "Automatic" counts only active channels, matching the server.
  • Edit takes the card's main button; the embed snippet sits beside it as a ghost button with no icon.
  • New strings in en.json and nb.json.

Docs. setup-chat-widget.md gets a "Link a voice channel" section: how the server picks a channel, the reason codes, and the tool call. Fixtures and openapi.json are regenerated.

Tests

New cases in widget.integration.test.ts:

  • linking, then checking the link survives unrelated updates (verified-identity toggle, allow-list edit), then unlinking with null;
  • rejecting an SMS channel, a deleted voice channel, another org's voice channel, the widget itself and an unknown id, each time leaving the current link alone;
  • the same link and rejection through PATCH /v1, which returns a 400 with the code;
  • creating a widget already linked, and checking a rejected create leaves no channel behind.

Results:

  • widget.integration.test.ts + widget-voice.integration.test.ts: 94 passed.
  • dashboard-pages: 395 passed.
  • Typecheck, eslint and the skill and PII checks are clean.
  • I also ran the dashboard locally and checked the card footer layout in the browser.

🤖 Generated with Claude Code

conv_create_widget_channel and conv_update_widget_channel take a
voiceChannelId, as do POST and PATCH /v1/conversations/channels/widget.
The id must be a Vapi or Threll voice channel in the same organization
that has not been deleted; anything else is refused with
conv_widget_voice_channel_invalid before it is saved. Until now the
link could only be set in the database.

Updating a widget channel no longer erases its voice link: the update
rebuilt the config from a fixed field list, so any edit dropped
voiceChannelId and calls fell back to
multiple_voice_channels_without_widget_routing.

The dashboard gains an Edit dialog for widget channels (origin
allow-list and voice channel), a voice channel picker on create, and a
note on each widget card saying where its calls go or why they are off.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@kmonsoe
kmonsoe merged commit f336f40 into main Oct 7, 2026
7 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