Previously, email replies were handled inside workers. There was no
execution logs. This meant if emails silently failed (as reported by a
customer), we had no way to trace where the issue happened, the only
assumption was “no error = mail sent.”
By moving email handling into jobs, we now have proper execution logs
for each attempt. This makes it easier to debug delivery issues and
would have better visibility when investigating customer reports.
Fixes
https://linear.app/chatwoot/issue/CW-5538/emails-are-not-sentdelivered-to-the-contact
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
Changes searchkick callback behavior to check `should_index?` before
creating reindex jobs, preventing unnecessary job creation for messages
that don't need indexing (activity messages, unpaid accounts, etc.).
Previously, `callbacks: :async` created reindex jobs for all messages
(~5,100/min or 7.3M/day in production), which were then filtered by
`should_index?` inside the job worker - resulting in 98% wasted jobs,
Redis memory pressure, and avoidable p0 alerts.
Now, `should_index?` is checked before job creation via `after_commit`
callback, reducing job creation to actual incoming/outgoing messages
from paid accounts.
Changes:
- Disable automatic searchkick callbacks
- Add manual `after_commit` callback with `should_index?` condition
- Add specs to verify callback behavior
Expected impact:
- 98% reduction in sidekiq job creation (~7.3M → ~150K jobs/day)
- Reduced redis memory usage
- Same async indexing behavior for eligible messages
With this change, the indexing would be separate from the search, so you
need to enable indexing on the cloud and run it. It should start
indexing the messages to ElasticSearch/OpenSearch. Once indexing is
completed, we can turn on the feature for the customer.
Make sure that the following is done when you deploy.
Set POSTGRES_STATEMENT_TIMEOUT=600s before you run the indexing.
1. Make sure that the account with advanced_search has
advanced_search_indexing enabled
```rb
Account.feature_advanced_search.each do |account|
account.enable_features(:advanced_search_indexing)
account.save!
end
```
2. Enable indexing for all accounts with paid subscription.
```rb
Account.where("custom_attributes ->> 'plan_name' IN (?)", ['Enterprise', 'Startups', 'Business']).each do |account|
account.enable_features(:advanced_search_indexing)
account.save!
end
```
3. Run indexing for all the messages.
```rb
Message.reindex
```
Co-authored-by: Vishnu Narayanan <iamwishnu@gmail.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This PR delivers the first slice of the voice channel: inbound call
handling. When a customer calls a configured voice
number, Chatwoot now creates a new conversation and shows a dedicated
call bubble in the UI. As the call progresses
(ringing, answered, completed), its status updates in real time in both
the conversation list and the call bubble, so
agents can instantly see what’s happening. This focuses on the inbound
flow and is part of breaking the larger voice
feature into smaller, functional, and testable units; further
enhancements will follow in subsequent PRs.
references: #11602 , #11481
## Testing
- Configure a Voice inbox in Chatwoot with your Twilio number.
- Place a call to that number.
- Verify a new conversation appears in the Voice inbox for the call.
- Open it and confirm a dedicated voice call message bubble is shown.
- Watch status update live (ringing/answered); hang up and see it change
to completed in both the bubble and conversation
list.
- to test missed call status, make sure to hangup the call before the
please wait while we connect you to an agent message plays
## Screens
<img width="400" alt="Screenshot 2025-09-03 at 3 11 25 PM"
src="https://github.com/user-attachments/assets/d6a1d2ff-2ded-47b7-9144-a9d898beb380"
/>
<img width="700" alt="Screenshot 2025-09-03 at 3 11 33 PM"
src="https://github.com/user-attachments/assets/c25e6a1e-a885-47f7-b3d7-c3e15eef18c7"
/>
<img width="700" alt="Screenshot 2025-09-03 at 3 11 57 PM"
src="https://github.com/user-attachments/assets/29e7366d-b1d4-4add-a062-4646d2bff435"
/>
<img width="442" height="255" alt="Screenshot 2025-09-04 at 11 55 01 PM"
src="https://github.com/user-attachments/assets/703126f6-a448-49d9-9c02-daf3092cc7f9"
/>
---------
Co-authored-by: Muhsin <muhsinkeramam@gmail.com>
We now support searching within the actual message content, email
subject lines, and audio transcriptions. This enables a faster, more
accurate search experience going forward. Unlike the standard message
search, which is limited to the last 3 months, this search has no time
restrictions.
The search engine also accounts for small variations in queries. Minor
spelling mistakes, such as searching for slck instead of Slack, will
still return the correct results. It also ignores differences in accents
and diacritics, so searching for Deja vu will match content containing
Déjà vu.
We can also refine searches in the future by criteria such as:
- Searching within a specific inbox
- Filtering by sender or recipient
- Limiting to messages sent by an agent
Fixes https://github.com/chatwoot/chatwoot/issues/11656
Fixes https://github.com/chatwoot/chatwoot/issues/10669
Fixes https://github.com/chatwoot/chatwoot/issues/5910
---
Rake tasks to reindex all the messages.
```sh
bundle exec rake search:all
```
Rake task to reindex messages from one account only
```sh
bundle exec rake search:account ACCOUNT_ID=1
```
The term "sorcerer’s apprentice mode" is defined as a bug in a protocol
where, under some circumstances, the receipt of a message causes
multiple messages to be sent, each of which, when received, triggers the
same bug. - RFC3834
Reference: https://github.com/chatwoot/chatwoot/pull/9606
This PR:
- Adds an auto_reply attribute to message.
- Adds an auto_reply attribute to conversation.
- Disable conversation_created / conversation_opened event if auto_reply
is set.
- Disable message_created event if auto_reply is set.
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This PR adds a composite index (:account_id, :content_type, :created_at)
on the table messages.
This index is added as a temporary fix for performance issues in the
CSAT responses controller where we query messages with account_id,
content_type and created_at. The current implementation
(account.message.input_csat.count) times out with millions of messages.
TODO: Create a dedicated csat_survey table and add entries when surveys
are sent, then query this table instead of the entire messages table for
better performance.
We made so many improvements for CSAT via https://github.com/chatwoot/chatwoot/pull/11485. However, we missed showing message content in the dashboard for CSAT URLs created in non-widget inboxes. This PR fixes the issue by ensuring that CSAT-configured messages are passed along with CSAT responses, otherwise defaulting to the translation.
Show captain messages under the name of the assistant which generated
the message.
- Add support for `Captain::Assistant` sender type
- Add push_event_data for captain_assistants
- Add activity message handler for captain_assistants
- Update UI to show captain messages under the name of the assistant
- Fix the issue where openAI errors when image is sent
- Add support for custom name of the assistant
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
The `before_type_cast` method sometimes returns a string for
`message_type`, creating inconsistencies in different payloads. This
pull request will remove all `before_type_cast` usage and replace it
with `to_i` methods.
- Adding a new index on (account_id,created_at,message_type) based on
our performance improvement exercise. This index significantly improves
the page load speeds of messaging reports.
This PR addresses several items listed in our rubocop_todo by implementing the necessary corrections and enhancements. As a result, we are now able to remove the rubocop_todo file entirely, streamlining our codebase and ensuring adherence to our coding standards.
fixes: https://linear.app/chatwoot/issue/CW-1806/chore-rubocop-audit
We observed an issue in production where the external webhook for an API inbox was failing. This, in turn, calls message update to update message status to failed. This causes a loop because rails trigger after_update callbacks even for empty commits.
Ref: rails/rails#44500
This PR logs additional information in content_attributes of a message in case it is unsupported. This info can be used by the client to render a fresh UI
Updating the `unattended` tab to include conversations where the customer responded and is awaiting an agent's response.
Previously it showed only the conversations where the first response was pending.
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
- This renames the unread_since scope to created_since, which more accurately describes what the scope returns.
- The EXTRACT(EPOCH FROM created_at) > (?) clause was also simplified and rewritten as created_at > ?, which is equivalent:
* logging the messages id for message validation exception
* Update the processed_message_content validation over length
* codeclimate
* specs failing for contacts
- Add JsonSchemaValidator, which takes a declarative schema and validates it for a given property.
- Add specs for JsonSchemaValidator
- Enable the validator for template_params
- Strip search term before searching
- order messages by created_at desc
- order contacts by last_activity_at desc
- order conversations by created_at desc
- Search only resolved contacts
- Optimize resolved contacts query
ref: #6583
* feat: ignore bots in avg_first_response_time
* feat: ignore bots in avg_first_response count
* feat: add bot handoff event
* feat: add handoff event listener and reporting event
* fix: ignore agent bot in first response
* refactor: calculate first_response with last handoff
* refactor: method defn order
* test: new reporting events
* feat: Revert "feat: ignore bots in avg_first_response count"
This reverts commit de1977c219a2e7a9180dd02272244fe3b3f7ce89.
* feat: Revert "feat: ignore bots in avg_first_response_time"
This reverts commit bb9171945d5e3b2f6015f4f96dd1b76b3efb6987.
* fix: business hour calculation for first_reply
* fix: event_start_time for first_response
* feat: add migration to recompute first_responses
* refactor: separate mute helpers for conversation
* refactor: rename migration
* refactor: migration script
* fix: migration typo
* fix: typo in query
* feat: update schema.rb
* Revert "feat: update schema.rb"
This reverts commit 353ef355f2d956dd219907bb66982dc90ca5d896.
* feat: update schema
* refactor: update events as a batch job
* fix: ignore the event if value is negative
* feat: don't create a new hand-off if it's already present
* refactor: break the action into smaller chunks
* refactor: update reporting listener spec
Handle the case to ensure extra bot handoffs are not created for a give conversation
* fix: import error
---------
Co-authored-by: Vishnu Narayanan <vishnu@chatwoot.com>