The backend overlay adds per-inbox voice agent configuration to Chatwoot’s Rails application. Two migrations extend theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/jAtInn71/chatwoot-costom/llms.txt
Use this file to discover all available pages before exploring further.
channel_web_widgets table, a model override registers the new attributes and feature flag, two controller overrides add voice-specific endpoints, and view files expose the new fields in the JSON API consumed by the widget and the dashboard.
Database migrations
Two migrations are shipped incustom/backend/migrations/ and copied into /app/db/migrate/ by the Dockerfile. Run them with the standard rails db:migrate (or allow the Rails boot sequence to run them on first start).
20260408000001_add_elevenlabs_to_channel_web_widgets.rb
Adds the elevenlabs_agent_id string column:
20260409000001_add_voice_agent_config_to_channel_web_widgets.rb
Adds three columns for provider-agnostic voice configuration:
| Column | Type | Purpose |
|---|---|---|
elevenlabs_agent_id | string | ElevenLabs public agent ID — also mirrored inside voice_agent_config_data.agent_id |
voice_agent_provider | string | Provider name, e.g. "elevenlabs" |
voice_agent_api_key | string | Provider API key, stored server-side only |
voice_agent_config_data | jsonb | Flexible JSON blob: agent_id, voice_id, agent_name, and any future fields |
Model — custom/backend/models/web_widget.rb
The model override registers all four columns in EDITABLE_ATTRS and declares the elevenlabs_voice feature flag at bit 5 using FlagShihTzu:
elevenlabs_voice. Toggle it from the dashboard Voice Agent configuration section — the widget checks selected_feature_flags.includes('elevenlabs_voice') to decide whether to show the call button.
voice_agent_config_data is listed as a bare :voice_agent_config_data symbol in EDITABLE_ATTRS — not as a hash with nested keys. Rails strong params silently drops nested hashes for bare-symbol entries, so the dashboard must serialize this field as a JSON string before submitting. The controller re-parses the string back to a hash (see below).Controllers
custom/backend/controllers/conversations_controller.rb
Overrides Api::V1::Widget::ConversationsController to add three voice-specific actions. The two that do not need a contact record skip the set_contact before-action:
inbox_config — GET /api/v1/widget/conversations/inbox_config
Returns all voice configuration for the current inbox, keyed by website_token. The widget calls this endpoint every time the iframe panel opens to pick up dashboard changes without a full page reload.
voice_transcript — POST /api/v1/widget/conversations/voice_transcript
Appends a single spoken turn from the live ElevenLabs call to the visitor’s Chatwoot conversation. The source parameter is "user" or "ai"; content is the transcribed text. If the visitor starts a voice call before sending any text message, the controller creates a new conversation tagged initiated_from: 'voice_agent':
content_attributes: { voice_transcript: true, role: source } so the dashboard and reports can style voice turns differently from text messages.
voice_signed_url — GET /api/v1/widget/conversations/voice_signed_url
Exchanges the inbox’s stored ElevenLabs API key for a short-lived signed WebSocket URL. The key never leaves the server — the widget receives only the signed URL, which it passes to ElevenLabs for private-agent sessions.
custom/backend/controllers/inboxes_controller.rb
Overrides Api::V1::Accounts::InboxesController to handle two edge cases in the channel update flow.
Feature flags as array. The dashboard may submit selected_feature_flags as a single string instead of an array; the controller normalises it:
voice_agent_config_data. Because EDITABLE_ATTRS lists :voice_agent_config_data as a bare symbol, strong params drops any nested hash. The dashboard serialises the value as a JSON string; the controller re-parses it at lines 150–157:
Views
custom/backend/views/_inbox.json.jbuilder
Extends the shared inbox JSON response to include all four voice agent fields. The view also handles the case where voice_agent_config_data is stored as a string rather than a parsed object:
custom/backend/views/show.html.erb
A custom widget embedding template at /app/app/views/widgets/show.html.erb. It provides the HTML shell that hosts the Vue widget bundle for the /custom-widget route.
Helper
custom/backend/controllers/concerns/website_token_helper.rb overrides the upstream concern of the same name. It provides token generation and validation for web widget authentication — every widget API request is scoped to a website_token query parameter that this concern resolves to the correct inbox and account.
Routes
custom/backend/routes.rb replaces the upstream routes file in full. The relevant additions are in the widget namespace:
ElevenLabsVoiceButton.vue and voiceAgentConfig.js.