* feat: add message update processing in IncomingMessageBaileysService
* feat: implement message update handling in IncomingMessageBaileysService
* feat: add MessageNotFoundError for handling invalid update messages in IncomingMessageBaileysService
* chore: specs for message.update events and bug fixes
* chore: refactor message update handling in IncomingMessageBaileysService
* chore: nit remove redundant comment
* chore: enhance logging for unsupported message update statuses in IncomingMessageBaileysService and disabled metrics
* chore: message status update logic with transition checks
* chore: update status mapping for PENDING to sent in status_mapper
* chore: update status_mapper comments and fix case statement for status codes
* fix: logging for unsupported message updates in update_message_content method
* test: add specs for unsupported status transitions in messages.update event
* refactor: ensure message status is reloaded before assertion in messages.update event spec
* refactor: status variable in status_mapper method
* refactor: rename status_transition_allowed method to status_transition_allowed?
* refactor: streamline message creation in specs by using let! for setup
* feat: process webhook whatsapp await response (#21)
* feat: enhance WhatsApp webhook processing and error responses handling
* chore: correct spelling of 'WhatsApp' in webhook controller specs
* refactor: rename webhook processing method and improve error handling
* chore: update error handling in WhatsApp controller specs for specific exceptions
* refactor: remove handling for StandardError in WhatsApp controller specs
* refactor: simplify perform_whatsapp_events_job method
* chore: update response status from not_found to bad_request for invalid message
* refactor: update expectations for job processing in WhatsApp controller specs
This PR introduces basic minimum version of **Instagram Business
Login**, making Instagram inbox setup more straightforward by removing
the Facebook Page dependency. This update enhances user experience and
aligns with Meta’s recommended best practices.
Fixes
https://linear.app/chatwoot/issue/CW-3728/instagram-login-how-to-implement-the-changes
## Why Introduce Instagram as a Separate Inbox?
Currently, our Instagram integration requires linking an Instagram
account to a Facebook Page, making setup complex. To simplify this
process, Instagram now offers **Instagram Business Login**, which allows
users to authenticate directly with their Instagram credentials.
The **Instagram API with Instagram Login** enables businesses and
creators to send and receive messages without needing a Facebook Page
connection. While an Instagram Business or Creator account is still
required, this approach provides a more straightforward integration
process.
| **Existing Approach (Facebook Login for Business)** | **New Approach
(Instagram Business Login)** |
| --- | --- |
| Requires linking Instagram to a Facebook Page | No Facebook Page
required |
| Users log in via Facebook credentials | Users log in via Instagram
credentials |
| Configuration is more complex | Simpler setup |
Meta recommends using **Instagram Business Login** as the preferred
authentication method due to its easier configuration and improved
developer experience.
---
## Implementation Plan
The core messaging functionality is already in place, but the transition
to **Instagram Business Login** requires adjustments.
### Changes & Considerations
- **API Adjustments**: The Instagram API uses `graph.instagram`, whereas
Koala (our existing library) interacts with `graph.facebook`. We may
need to modify API calls accordingly.
- **Three Main Modules**:
1. **Instagram Business Login** – Handle authentication flow.
2. **Permissions & Features** – Ensure necessary API scopes are granted.
3. **Webhooks** – Enable real-time message retrieval.

---
## Instagram Login Flow
1. User clicks **"Create Inbox"** for Instagram.
2. App redirects to the [Instagram Authorization
URL](https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/business-login#embed-the-business-login-url).
3. After authentication, Instagram returns an authorization code.
5. The app exchanges the code for a **long-lived token** (valid for 60
days).
6. Tokens are refreshed periodically to maintain access.
7. Once completed, the app creates an inbox and redirects to the
Chatwoot dashboard.
---
## How to Test the Instagram Inbox
1. Create a new app on [Meta's Developer
Portal](https://developers.facebook.com/apps/).
2. Select **Business** as the app type and configure it.
3. Add the Instagram product and connect a business account.
4. Copy Instagram app ID and Instagram app secret
5. Add the Instagram app ID and Instagram app secret to your app config
via `{Chatwoot installation
url}/super_admin/app_config?config=instagram`
6. Configure Webhooks:
- Callback URL: `{your_chatwoot_url}/webhooks/instagram`
- Verify Token: `INSTAGRAM_VERIFY_TOKEN`
- Subscribe to `messages`, `messaging_seen`, and `message_reactions`
events.
7. Set up **Instagram Business Login**:
- Redirect URL: `{your_chatwoot_url}/instagram/callback`
8. Test inbox creation via the Chatwoot dashboard.
## Troubleshooting & Common Errors
### Insufficient Developer Role Error
- Ensure the Instagram user is added as a developer:
- **Meta Dashboard → App Roles → Roles → Add People → Enter Instagram
ID**
### API Access Deactivated
- Ensure the **Privacy Policy URL** is valid and correctly set.
### Invalid request: Request parameters are invalid: Invalid
redirect_uri
- Please configure the Frontend URL. The Frontend URL does not match the
authorization URL.
---
## To-Do List
- [x] Basic integration setup completed.
- [x] Enable sending messages via [Messaging
API](https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/messaging-api).
- [x] Implement automatic webhook subscriptions on inbox creation.
- [x] Handle **canceled authorization errors**.
- [x] Handle all the errors
https://developers.facebook.com/docs/instagram-platform/instagram-graph-api/reference/error-codes
- [x] Dynamically fetch **account IDs** instead of hardcoding them.
- [x] Prevent duplicate Instagram channel creation for the same account.
- [x] Use **Global Config** instead of environment variables.
- [x] Explore **Human Agent feature** for message handling.
- [x] Write and refine **test cases** for all scenarios.
- [x] Implement **token refresh mechanism** (tokens expire after 60
days).
Fixes https://github.com/chatwoot/chatwoot/issues/10440
---------
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
* fix: resolve bug for contact name when user stats a new conversation with a new contact
* chore: update specs to cover the bug resolution
* chore: correct variable name
* chore: simplify update contact name handling
* chore: ensure pushName is always a string and leave a comment to keep an eye if always pushName will be present
* fix: update contact name access
* chore: identify and permit only direct users jid types (#14)
* fix: add jid_type method to classify message sender types and prevent processing for non-user jids
* chore: update jid parsing to correctly extract phone number and improve message type detection
* chore: add spec to ensure no conversation is created for non-user messages
* chore: update jid shape comment
* chore: refactor jid_type method
* chore: update message_type logic for classify text messages when is a extendedTextMessage
* refactor: update spec that no conversation is created for non-user messages
* fix: use update! method for contact name update
* chore: add note on distinguishing broadcast and status JIDs in Baileys
* chore: add note on Baileys internal function for JID classification
* feat: baileys provider and placeholder for link device modal
* chore: drop qrcode.vue in favor of just img tag
* chore: update modal props
* feat: setup channel provider connection
* chore: update .env.example with Baileys API default configuration
* feat: add support for Baileys provider in WhatsApp events processing
* chore: rename Baileys API default host variable to DEFAULT_BAILEYS_URL
* feat: add setup and disconnect methods for Baileys channel provider in inboxes controller that will be implemented
* feat: add CHANNEL_CONNECTION_UPDATE event and include it in broadcast data preparation
* refactor: simplify channel retrieval logic in WhatsappEventsJob
* refactor: revert CHANNEL_UPDATE_EVENTS constant from ActionCableBroadcastJob
* feat: add 'baileys' as a provider option in Whatsapp channel model
* feat: add provider_connection field to Whatsapp channel model and migration
* refactor: remove unnecessary CHANNEL_CONNECTION_UPDATE event type
* feat: implement channel provider connection with baileys API
* feat: add inbox association to Whatsapp channel model and update webhook URL handling
* feat: enhance Baileys service to handle webhook multiple event types
* refactor: simplify webhook verification logic in Baileys service
* feat: add setup channel provider call, and refactor some logic
* chore: adapt logic to new API
* refactor: fix typo
* refactor: fix import
* refactor: fix typo
* chore: add fixme comment about race condition
* fix: remove double disconnect call
* feat: implement message processing for incoming WhatsApp messages
* refactor: streamline message type determination and improve readability
* chore: increase cache key granularity
provider connection info might be updated multiple times within 1 second, so updates might be lost due to cache key not being updated. changing cache key to milliseconds solves this
* feat: add `is-loading` to buttons
* feat: update send_message method to use 'to' parameter and improve error handling
* refactor: simplify test setup and update API key in specs
* chore: add setup and disconnect channel provider specs
* test: fix spec after increase cache key granularity
* feat: handle reconnecting state on modal
* style: centered error text
* feat: advanced options on create inbox
* feat: handle new reconnecting on backend
* refactor: update inbox controller specs and leave a FIXME note
* test: add specs for Whatsapp::IncomingMessageBaileysService
* feat: add baileys configuration page
* feat: link device button when disconnected on conversation
* chore: refactor .env.example
* feat: add TODO for unimplemented methods in IncomingMessageBaileysService
* fix: correct method name and update environment variable references in WhatsappBaileysService
* refactor: simplify channel lookup by removing redundant method and handling phone number check directly
* chore: add TODO for unimplemented event processing methods in IncomingMessageBaileysService
* fix: update environment variable references in WhatsappBaileysService tests
* chore(webhook): add pt-BR translations
* chore: add pt-br translations
* chore: inboxname component margin
* refactor: inboxname computed prop
* feat: enhance WhatsApp provider connection handling and message processing
* test: inbox controller
* chore: improve baileys connection and messages handling
* test: incoming message service baileys
* refactor: update provider config validation and improve test setup for WhatsApp Baileys service
* fix: ensure only text messages are sent and update message source ID
* fix: create message
* fix: only update message on success
* test: fix broken specs
* chore: raise error on unsupported message content type
* feat: hide provider connection data from non-admins
* fix: update advanced options
* chore: move class definition
* fix: issue with send_message not returning id
---------
Co-authored-by: gabrieljablonski <contact@gabrieljablonski.com>
* chore: exclude inbox_id from webhook update parameters
* feat: display inbox label in webhook settings
* feat: add inbox selection to webhook form
* feat: prevent updating inbox_id in webhook update
* feat: integrate MultiselectDropdown for inbox selection in webhook form
* feat: add inbox matching logic to webhook event delivery
* feat: remove unused inbox input placeholder from webhook form
* fix: MultiselectDropdown component submiting form
* feat: refine webhook parameters for create and update actions
* feat: disable URL input field when editing webhook
* chore: remove unnecessary parentheses
* chore: update webhook controller spec to ignore inbox_id and url updates
* fix: clean up JSON formatting
* fix: standardize inbox_id to inboxId in WebhookForm component
* refactor: replace LabelItem with InboxName component in WebhookRow
* chore: enhance MultiselectDropdown with button variant prop and update styling in WebhookForm
* chore: simplify MultiselectDropdown wrapper in WebhookForm component
* chore: update selectedInbox initialization to null and reorder form fields
* refactor: simplify InboxName
* chore: add dark variant styling for buttons in SCSS
* test: add inbox filtering for webhook event triggers in WebhookListener
* test: refactor webhook controller spec to use a variable for URL and improve update expectations
* feat(webhook): all inboxes option
* chore: remove dark variant styling for buttons in SCSS
* fix: bad interaction multiselectdropdown inside label
* chore: invert if
* chore: rename to assignedInbox and drop inboxId
* refactor(WebhookForm): restore div separating fields from buttons
* test: improve description
---------
Co-authored-by: gabrieljablonski <contact@gabrieljablonski.com>
* feat(migration): add name column to webhook table
* feat(webhooks): add name parameter to webhook params
* feat(webhooks): add example webhook name constant and input field to form
* fix(webhooks): add webhook name label and placeholder to multiple locales in the form
* feat(webhooks): display webhook name in the UI and include it in the API response
* Revert 'fix(webhooks): add webhook name label and placeholder to multiple locales in the form'
This reverts commit e547778a1c038c934e22ceb25935f541cb09e2cd.
* test(webhooks): add tests for creating and updating webhooks with name attribute
* chore(webhooks): add name property to webhook definitions in Swagger documentation
* chore(webhooks): remove unnecessary input touch event for webhook name field
* chore(webhooks): apply review changes requested
* chore(webhooks): revert auto lint changes in commit 18ec4cafeb72fd385b70f65f1873d7cfb65216a6
- Removes the portal_members table and all associated records
- Updates policies to use custom roles with knowledge_base_manage
permission
- Updates controllers, models, and views to work without portal
membership
- Adds tests for the new permission model
## Description
Add account delete option in the user account settings.
Fixes#1555
## Type of change
- [ ] New feature (non-breaking change which adds functionality)


## Checklist:
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
Co-authored-by: Sojan Jose <sojan.official@gmail.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
1. Add permission filter service to separate permission filtering logic
from conversation queries
2. Implement hierarchical permissions with cleaner logic:
- conversation_manage gives access to all conversations
- conversation_unassigned_manage gives access to unassigned and user's
conversations
- conversation_participating_manage gives access only to user's
conversations
---------
Co-authored-by: Pranav <pranav@chatwoot.com>
The expected payload on WhatsApp Cloud API is the following.
```json
{
"template": {
"name": "TEMPLATE_NAME",
"language": {
"code": "LANGUAGE_AND_LOCALE_CODE"
},
"components": [
"<NAMED_PARAMETER_INPUT>",
"<POSITIONAL_PARAMETER_INPUT>"
]
}
}
```
Named templates expect a `parameter_name`
```json
{
"type": "body",
"parameters": [
{
"type": "text",
"parameter_name": "customer_name",
"text": "John"
},
{
"type": "text",
"parameter_name": "order_id",
"text": "9128312831"
}
]
}
```
In this PR, we would check if the template is a name template, then we
would send the `parameter_name` as well.
Reference: https://github.com/chatwoot/chatwoot/issues/10886
# Pull Request Template
## Description
This PR will replace the upgrade banner with an upgrade page view.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
### Loom video
https://www.loom.com/share/0f2b4b09acdd4404bf3211184a470227?sid=7ed60a99-0299-4642-b907-2af8c4dcc643
## Checklist:
- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules
---------
Co-authored-by: Pranav <pranavrajs@gmail.com>
- Add a job to remove stale contacts and contact_inboxes across all accounts
Stale anonymous contact is defined as
- have no identification (email, phone_number, and identifier are NULL)
- have no conversations
- are older than 30 days
---------
Co-authored-by: Pranav <pranav@chatwoot.com>
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
Optimization #11183 missed a condition where the inbox_id filter is
manually passed. Due to the previous change, the inbox filter was being
discarded for admins, although it continued to work correctly for
agents.
This PR includes a fix for that specific case and adds a spec to
explicitly test it.
This pull request includes significant changes to the filtering logic
for conversations in the frontend, here's a summary of the changes
This includes adding a `matchesFilters` method that evaluates a
conversation against the applied filters. It does so by first evaluating
all the conditions, and later converting the results into a JSONLogic
object that can be evaluated according to Postgres operator precedence
### Alignment Specs
To ensure the frontend and backend implementations always align, we've
added tests on both sides with same cases, for anyone fixing any
regressions found in the frontend implementation, they need to ensure
the existing tests always pass.
Test Case | JavaScript Spec | Ruby Spec | Match?
-- | -- | -- | --
**A AND B OR C** | Present | Present | Yes
Matches when all conditions are true | Present | Present | Yes
Matches when first condition is false but third is true | Present |
Present | Yes
Matches when first and second conditions are false but third is true |
Present | Present | Yes
Does not match when all conditions are false | Present | Present | Yes
**A OR B AND C** | Present | Present | Yes
Matches when first condition is true | Present | Present | Yes
Matches when second and third conditions are true | Present | Present |
Yes
**A AND B OR C AND D** | Present | Present | Yes
Matches when first two conditions are true | Present | Present | Yes
Matches when last two conditions are true | Present | Present | Yes
**Mixed Operators (A AND (B OR C) AND D)** | Present | Present | Yes
Matches when all conditions in the chain are true | Present | Present |
Yes
Does not match when the last condition is false | Present | Present |
Yes
---------
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Pranav <pranav@chatwoot.com>
- Add dynamic importing for routes.
- Added caching for `campaign`, `articles` and `inbox_members` API end
points.
---------
Co-authored-by: Pranav <pranavrajs@gmail.com>
In this PR https://github.com/chatwoot/chatwoot/pull/11139, if there is
an attempt to create a duplication session for the contact in the same
inbox, we will anonymize the old session.
This PR would prevent sending messages to the older sessions. The
support agents will have to create a new conversation to continue
messages with customer.
This PR addresses a race condition in the contact inbox model caused by
duplicate `source_id` values linked to different contacts.
The issue typically occurs when an agent updates a contact’s email or
phone number or when two contacts are merged. In these scenarios, the
`source_id`, which is intended to uniquely identify the contact in a
session, may still be associated with the old contact inbox.
To solve this, we check if there’s already a ContactInbox with the same
source_id but linked to another contact. If we find one, we update that
old record by changing its source_id to a random value. This breaks the
wrong connection and prevents issues, while still keeping the old data
safe.
However, this is only a temporary fix. The main issue is with the way
the contact inbox model is designed. Right now, it’s being used to track
sessions, but that may not be necessary for non-live chat channels. In
the long run, we should consider redesigning this part of the system to
avoid such problems.
This PR implements the following features
- FAQs from conversations will be generated in account language
- Contact notes will be generated in account language
- Copilot chat will respond in user language, unless the agent asks the
question in a different language
## Changes
### Copilot Chat
- Update the prompt to include an instruction for the language, the bot
will reply in asked language, but will default to account language
- Update the `ChatService` class to include pass the language to
`SystemPromptsService`
### FAQ and Contact note generation
- Update contact note generator and conversation generator to include
account locale
- Pass the account locale to `SystemPromptsService`
<details><summary>Screenshots</summary>
#### FAQs being generated in system langauge

#### Copilot responding in system language

</details>
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Pranav <pranav@chatwoot.com>
This PR adds native integration with Shopify. No more dashboard apps.
The support agents can view the orders, their status and the link to the
order page on the conversation sidebar.
This PR does the following:
- Create an integration with Shopify (a new app is added in the
integrations tab)
- Option to configure it in SuperAdmin
- OAuth endpoint and the callbacks.
- Frontend component to render the orders. (We might need to cache it in
the future)
---------
Co-authored-by: iamsivin <iamsivin@gmail.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This PR updates the search implementation to better utilize the GIN
indexes. The option is toggled behind a feature flag for us to test it
internally before making it available publicly
The issue was that when a team change results in an assignee being set
to nil, the system was still trying to create a notification about the
assignment change, but there was no assignee to notify, causing
potential issues in the notification system.
The UI displays only six articles, and this update introduces a per_page
parameter to control the number of articles returned per API call. The
value is capped between 1 and 100, with a default fallback if a lower
number is set.
This change is necessary due to high website traffic, where excessive
payloads are returned without adding value.
**Changes:**
- Add index to status, account_id, portal_id, views.
- Add per_page param in the API.
- Update the code in the frontend to fetch only 6
- Twilio events were being processed synchronously, leading to slow API
responses.
- This change moves Twilio event processing to a background job to
improve performance and align with how other events (e.g., WhatsApp) are
handled.
---------
Co-authored-by: Pranav <pranav@chatwoot.com>
At 5 PM, when business hours officially end, an automatic out-of-office
message is sent to customers. However, this creates a poor experience if
an agent is actively chatting with the customer. This update ensures
that the out-of-office message is only sent if no agent message has been
sent in the last 5 minutes. If the customer reaches out again after 5
minutes, the out-of-office message will be triggered.
- Add a warning logger for cases where we are getting webhook events for
inactive numbers.
- Add config to discard events for inactive numbers so that the meta
will stop sending events
---------
Co-authored-by: Pranav <pranav@chatwoot.com>
We allow users to select locale variants when creating the help center
(e.g., pt_BR or en_UK). However, the selected variant may not always be
available for translation in the app. In such cases, we need to fall
back to either the base language or the default locale.
While this fallback logic was implemented for the portal locale, it was
missing for article locales.
This PR fixes that issue.
Fixes
https://linear.app/chatwoot/issue/CW-4122/slackwebapierrorsmissingscope-missing-scope
This PR adds the ability to handle errors when scopes are missing during
link unfurling. Since link unfurling is just a nice-to-have feature that
doesn't affect core functionality, we will silently ignore these errors.
---------
Co-authored-by: Sojan <sojan@pepalo.com>
This PR adds missing error handlers for the following channels and cases
1. WhatsApp - Generic Handlers for both Cloud and 360Dialog (Deprecated)
2. Instagram - Handler for a case where there is an HTTP error instead
of an `:error` in the 200 response
3. Facebook - Errors from the two sentry issues
([Net::OpenTimeout](https://chatwoot-p3.sentry.io/issues/6164805227) &
[JSON::ParserError](https://chatwoot-p3.sentry.io/issues/5903200786))
4. SMS: Generic handlers for Bandwidth SMS
#### Checklist
- [x] Bandwidth SMS
- [x] Whatsapp Cloud + 360 Dialog
- [x] Twilio SMS
- [x] Line
- [x] Telegram
- [x] Instagram
- [x] Facebook
- [x] GMail
- [x] 365 Mail
- [x] SMTP Mail
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This PR adds service to automate account abuse detection. Currently
based on the signup name and URL, could potentially add more context
such as usage analysis, message metadata etc.
In Chatwoot, we rely on the Content-ID for inline attachments to replace
the link with the uploaded attachment URL. Our expectation was that only
images would be inline, while other attachments would not. However,
email clients like Apple Mail (sigh) allow users to send inline
attachments that are not images, and these attachments often lack a
Content-ID. This creates significant issues in rendering.
I investigated how other email clients handle this scenario. When
viewing the same email (sent from Apple Mail) in Gmail, only one image
appears—and it’s treated as an attachment, not inline. This happens
because both attachments are the same image, and Apple Mail only sends
one copy. See the screenshot below.
| Apple Mail | Gmail |
| -- | -- |
| <img width="646" alt="Screenshot 2025-02-27 at 8 20 17 PM"
src="https://github.com/user-attachments/assets/e0d1cd2d-e47c-4081-a53b-7a67106341b3"
/> | <img width="360" alt="Screenshot 2025-02-27 at 8 20 51 PM"
src="https://github.com/user-attachments/assets/b206e56e-8f86-43e9-867b-d895c36aff78"
/> |
A good fix for this would be to check if the Content-ID is missing and
then upload the file as a regular attachment. However, the Mail gem (for
some reason) automatically adds a default Content-ID to inline parts. I
need to dig into the source code to understand why this happens.
For now, I’ve implemented a check to treat non-image attachments as
regular attachments. Inline image attachments are already handled by
appending an image tag at the end if the content-id is not found in the
body. A sample conversation to test this behavior is
[here](https://app.chatwoot.com/app/accounts/1/conversations/46732).
This PR is part of the larger #10849 implementation and introduces a new
Live Reports API to provide real-time conversation metrics.
The /live_reports/conversation_metrics endpoint returns account-level or
team-level conversation statistics, including open, pending, unattended,
and unassigned conversation counts.
The /live_reports/grouped_conversation_metrics endpoint accepts a group
parameter, either team_id or assignee_id, and returns open and
unattended conversation counts based on the specified grouping.
Fixes https://linear.app/chatwoot/issue/CW-3417/oauth-20-authentication
We are planning to publish the Chatwoot app in the Linear [integration
list](https://linear.app/docs/integration-directory). While we currently
use token-based authentication, Linear recommends OAuth2 authentication.
This PR implements OAuth2 support.
---------
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
At the moment, when updating the inbox members, or team members the
account cache used for IndexedDB is not invalidated. This can cause
inconsistencies in the UI. This PR fixes this by adding explicit
invalidation after performing the member changes
### Summary of changes
1. Added a new method `add_members` and `remove_members` to both `team`
and `inbox` models. The change was necessary for two reasons
- Since the individual `add_member` and `remove_member` is called in a
loop, it's wasteful to run the cache invalidation in the method.
- Moving the account cache invalidation call in the controller pollutes
the controller business logic
2. Updated tests across the board
### More improvements
We can make a concern called `Memberable` with usage like
`memberable_with :inbox_members`, that can encapsulate the functionality
---
Related: https://github.com/chatwoot/chatwoot/issues/10578
# Pull Request Template
## Description
Dyte V1 API's are soon going to be deprecated, hence making sure we
update Chatwoot before that happens
Fixes#10704
## Type of change
Please delete options that are not relevant.
- [x] New feature (non-breaking change which adds functionality)
## How Has This Been Tested?
1. Open a new or existing conversation from the inbox
2. Press the video call icon on the message composer
3. Verify that the message dialog shows up with the join video call
button
4. Verify that clicking on join call does join the call
## Checklist:
- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] My changes generate no new warnings
- [ ] New and existing unit tests pass locally with my changes (Unable
to run this locally)
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
The Slack `files.upload` API endpoint is deprecated and will stop
functioning on March 11, 2025. In this PR, we have implemented the
changes for the [new file
upload](https://api.slack.com/messaging/files#uploading_files) method.
This is the error that is triggering a P0 incident in Chatwoot.
```
DashboardController#index is missing a template for this request format and variant. request.formats: ["application/json"] request.variant: []
```
The user is calling `/app/accounts/api/v1/accounts/<account-id>/inboxes`. The URL is wrong, the requests are routed to dashboard controller as it starts with `/app/accounts`. The dashboard controller is not handling JSON requests and it creates errors. There are 312k errors over the last 2 years. Close to 50k during last 3 days.
This fix would return not_acceptable response to the attempts.
# Pull Request Template
## Description
This PR adds the ability to see the shared contact name in Telegram
channels.
## Type of change
- [x] New feature (non-breaking change which adds functionality)
## How Has This Been Tested?
**Loom video**
https://www.loom.com/share/cd318056ad4d44d4a1fc4b5d4ad38d60?sid=26d833ae-ded9-4cf0-9af7-81eecfa37f19
## Checklist:
- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules
---------
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
# Pull Request Template
## Description
This PR adds support for displaying shared contacts in a Telegram
channel.
**NB:** Tested with both old and new bubbles.
Multiple numbers for a single contact are not supported at this time,
but multiple contacts are supported.
In the future, we can add support for displaying contact names as well.
## Type of change
- [x] New feature (non-breaking change which adds functionality)
## How Has This Been Tested?
**Loom video**
https://www.loom.com/share/95efadace3194887bc0663c53e7c08bc?sid=a5c27176-3dd8-456c-80b9-c63dbb89dca1
## Checklist:
- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [ ] I have commented on my code, particularly in hard-to-understand
areas
- [ ] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules
The update ensures proper handling of text updates in photo/video
messages by accounting for the caption attribute in addition to the text
attribute. This change enables consistent processing across both
messages.
Fixes https://github.com/chatwoot/chatwoot/issues/10760
Note: TIL, you can update the video/photo you’ve sent on Telegram, not
just the text. Currently, we’re not handling this. To support it, we
need to parse the payload and update the attachments accordingly. This
could be taken as a followup.
We have been encountering errors in the community pipeline for the
contacts sort by country spec. Upon investigation, it was discovered
that the spec assumes the country code is used for sorting. However, the
sorting actually relies on the country attribute.
The payload from a previous spec run indicates that none of the contact
objects include the country attribute. This fix addresses the issue by
aligning the spec with the actual implementation logic.
Here’s an example payload from the previous spec run for reference:
<img width="1063" alt="Screenshot 2025-01-31 at 6 17 44 PM"
src="https://github.com/user-attachments/assets/df9b26e8-c5d0-432a-9d2a-16f76cb66967"
/>
This PR ensures that only conversations from quick conversation channels
are resolved, avoiding resolutions on the email channel (we still need
to improve the UX here). It also updates the FAQ generation logic,
limiting it to conversations that had at least one human interaction.
This PR updates the report pages for agents, inboxes, and teams by
replacing charts with aggregate values (under a feature flag). Users can
click on any item to view more details if needed. Most users seem to
prefer aggregate values, so this change will likely stay.
The PR also includes a few fixes:
- The summary reports now use the same logic for both the front-end and
CSV exports.
- Fixed an issue where a single quote was being added to values with
hyphens in CSV files. Now, ‘n/a’ is used when no value is available.
- Fixed a bug where the average value was calculated incorrectly when
multiple accounts were present.
These changes should make reports easier to use and more consistent.
### Agents:
<img width="1438" alt="Screenshot 2025-01-26 at 10 47 18 AM"
src="https://github.com/user-attachments/assets/bf2fcebc-6207-4701-9703-5c2110b7b8a0"
/>
### Inboxes
<img width="1438" alt="Screenshot 2025-01-26 at 10 47 10 AM"
src="https://github.com/user-attachments/assets/b83e1cf2-fd14-4e8e-8dcd-9033404a9f22"
/>
### Teams:
<img width="1436" alt="Screenshot 2025-01-26 at 10 47 01 AM"
src="https://github.com/user-attachments/assets/96b1ce07-f557-42ca-8143-546a111d6458"
/>
---------
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
This PR introduces several improvements to the Captain AI dashboard
section:
- New billing page, with new colors, layout and meters for Captain usage
- Updated the base paywall component to use new colors
- Updated PageLayout.vue, it's more generic and can be used for other
pages as well
- Use flags to toggle empty state and loading state
- Add prop for `featureFlag` to show the paywall slot based on feature
enabled on account
- Update `useAccount` to add a `isCloudFeatureEnabled`
- **Removed feature flag checks from captain route definitions**, so the
captain entry will always be visible on the sidebar
- Add banner to Captain pages for the following cases
- Responses usage is over 80%
- Documents limit is fully exhausted
### Screenshots
<details><summary>Free plan</summary>
<p>


</p>
</details>
<details><summary>Paid plan</summary>
<p>


</p>
</details>
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
Co-authored-by: Pranav <pranav@chatwoot.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
- Fixed Firecrawl webhook payloads to ensure proper data handling and
delivery.
- Removed unused Robin AI code to improve codebase cleanliness and
maintainability.
- Implement authentication for the Firecrawl endpoint to improve
security. A key is generated to secure the webhook URLs from FireCrawl.
---------
Co-authored-by: Pranav <pranavrajs@gmail.com>
This pull request introduces several changes to implement and manage
usage limits for the Captain AI service. The key changes include adding
configuration for plan limits, updating error messages, modifying
controllers and models to handle usage limits, and updating tests to
ensure the new functionality works correctly.
## Implementation Checklist
- [x] Ability to configure captain limits per check
- [x] Update response for `usage_limits` to include captain limits
- [x] Methods to increment or reset captain responses limits in the
`limits` column for the `Account` model
- [x] Check documents limit using a count query
- [x] Ensure Captain hand-off if a limit is reached
- [x] Ensure limits are enforced for Copilot Chat
- [x] Ensure limits are reset when stripe webhook comes in
- [x] Increment usage for FAQ generation and Contact notes
- [x] Ensure documents limit is enforced
These changes ensure that the Captain AI service operates within the defined usage limits for different subscription plans, providing appropriate error messages and handling when limits are exceeded.
Currently, it’s unclear whether an FAQ item is generated from a
document, derived from a conversation, or added manually.
This PR resolves the issue by providing visibility into the source of
each FAQ. Users can now see whether an FAQ was generated or manually
added and, if applicable, by whom.
- Move the document_id to a polymorphic relation (documentable).
- Updated the APIs to accommodate the change.
- Update the service to add corresponding references.
- Updated the specs.
<img width="1007" alt="Screenshot 2025-01-15 at 11 27 56 PM"
src="https://github.com/user-attachments/assets/7d58f798-19c0-4407-b3e2-748a919d14af"
/>
---------
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
This PR introduces a review step for generated FAQs, allowing a human to
validate and approve them before use in customer interactions. While
hallucinations are minimal, this step ensures accurate and reliable FAQs
for Captain to use during LLM calls when responding to customers.
- Added a status field for the FAQ
- Allow the filter on the UI.
<img width="1072" alt="Screenshot 2025-01-15 at 6 39 26 PM"
src="https://github.com/user-attachments/assets/81dfc038-31e9-40e6-8a09-586ebc4e8384"
/>
Migration Guide: https://chwt.app/v4/migration
This PR imports all the work related to Captain into the EE codebase. Captain represents the AI-based features in Chatwoot and includes the following key components:
- Assistant: An assistant has a persona, the product it would be trained on. At the moment, the data at which it is trained is from websites. Future integrations on Notion documents, PDF etc. This PR enables connecting an assistant to an inbox. The assistant would run the conversation every time before transferring it to an agent.
- Copilot for Agents: When an agent is supporting a customer, we will be able to offer additional help to lookup some data or fetch information from integrations etc via copilot.
- Conversation FAQ generator: When a conversation is resolved, the Captain integration would identify questions which were not in the knowledge base.
- CRM memory: Learns from the conversations and identifies important information about the contact.
---------
Co-authored-by: Vishnu Narayanan <vishnu@chatwoot.com>
Co-authored-by: Sojan <sojan@pepalo.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
This PR addresses an issue where mention_notification was being
overwritten by assigned_conversation_new_message notifications.
Similarly, participating_conversation_new_message was taking priority
over assigned_conversation_new_message.
The fix ensures that additional notifications are not created in these
scenarios by establishing a priority order for notifications as follows:
1. mention_notification
2. assigned_conversation_new_message
3. participating_conversation_new_message
This change maintains consistency and prevents redundant notifications.
----
Note: In the older implementation of notifications in Chatwoot,
notification objects were created only if the user had subscribed to a
specific notification type. With the new inbox_view model, we are
changing this approach to create notification objects in all cases. This
PR updates the services to remove reliance on notification preferences,
simplifying the logic.
----
Caveat: Since we don’t create extra notifications for new messages if a
mention_notification already exists, a user subscribed to
new_message_notification push/email notifications but not to
mention_notification will not receive notifications in these cases.
- Prevents notifications from being created for conversations or actions
involving blocked contacts.
- The exception is the conversation_mention notification type, which
will still be created when applicable.
Following https://github.com/chatwoot/chatwoot/pull/10604, this PR
introduces similar reporting features for Agents and Teams.
Updates in this PR:
- Added additional methods to the base class to avoid repetition.
- Improve reporting for Teams and Agents to include resolution count.
The Inbox Overview section is being updated to offer a more detailed
report, showing an overall view of the account grouped by inboxes. To
view detailed reports and access specific graphs for individual inboxes,
click on the inbox name to navigate to its dedicated report page.
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
Added the possibility to mark as a single conversation in the API type
inbox. This allows the conversation builder to search for the last
conversation.
I thought about searching for the last conversation with created_at:
desc order, as is done in some channels... but I didn't change the way
the conversation is searched.
Fixes: #7726
Co-authored-by: Sojan Jose <sojan@pepalo.com>
This PR resolves the issue with updating the profile picture in the profile settings.
**Cause of issue**
The issue can be reproduced with the old `ProfileAvatar.vue` component.
While the exact reason is unclear, it seems related to cases where the
file might be `null`.
**Solution**
Replaced the old `ProfileAvatar.vue` with `Avatar.vue` and tested it. It
works fine. I’ve attached a loom video below.
Fixes https://linear.app/chatwoot/issue/CW-3768/profile-picture-bug
Co-authored-by: Pranav <pranav@chatwoot.com>
Co-authored-by: Pranav <pranavrajs@gmail.com>
Skip calling the Slack file upload API for message types such as
fallback (e.g., Facebook and location messages) that lack actual file
data in attachments. This prevents unnecessary API calls and resolves a
Sentry error currently occurring in production.
fixes: https://github.com/chatwoot/chatwoot/issues/10460
Invalid urls supplied to the job was causing sentry issues. The issue primarily occurs when the download file.original_filename comes out as empty
fixes: https://github.com/chatwoot/chatwoot/issues/10449
This commit fixes the issue with Line stickers URLs to prevent certain
images from failing to display. The problem was due to the use of
incorrect URLs. The original URLs pointed to the `iphone` variant, which
failed to load properly in some cases. The fix updates the URLs to use
the `android` variant, ensuring all images are displayed correctly.
### Example:
- Original (failing URL):
`https://stickershop.line-scdn.net/stickershop/v1/sticker/17/iphone/sticker.png`
- Fixed (working URL):
`https://stickershop.line-scdn.net/stickershop/v1/sticker/17/android/sticker.png`
## How Has This Been Tested?
1. Verified the updated URLs by loading multiple Line sticker images to
ensure they display correctly.
2. Tested in both local and production-like environments to confirm the
fix resolves the issue.
3. Reviewed logs to ensure no additional errors are generated related to
Line sticker URLs.
- We observed in prod for certain emails active storage blob objects
were getting created with empty file name. The conversations further
causes conversation and filter pages to break. This change will fix the
mentioned issue.
fixes:
https://linear.app/chatwoot/issue/CW-3331/missing-file-name-for-some-of-the-uploads-for-emails
---------
Co-authored-by: Pranav <pranavrajs@gmail.com>
Some email clients automatically set Content-Disposition to inline for
specific content types, such as images. In cases where the email body is
empty, inline attachments may not display correctly due to our previous
implementation. Our assumption was that these attachments are referenced
within text/plain or text/html parts.
Customer-reported issues, especially with Apple Mail, show emails with
attachments marked as inline but without any corresponding text parts.
This leads to missing attachments even though would have processed the
attachment.
This update introduces a check for the presence of a text part. If none
exists, inline attachments are treated as regular attachments and added
to the external attachments array, ensuring that all attachments display
properly.
<details>
<summary><b>Script to update the existing emails that are already
available in the system</b></summary>
```rb
def update_content id
message = Message.find id
conversation = message.conversation
message_id = message.source_id
channel = message.inbox.channel
authentication_type = 'XOAUTH2'
imap_password = Google::RefreshOauthTokenService.new(channel: channel).access_token
imap = Net::IMAP.new(channel.imap_address, port: channel.imap_port, ssl: true)
imap.authenticate(authentication_type, channel.imap_login, imap_password)
imap.select('INBOX')
results = imap.search(['HEADER', 'MESSAGE-ID', message_id])
message_content = imap.fetch(results.first, 'RFC822').first.attr['RFC822']
mail = MailPresenter.new(Mail.read_from_string(message_content))
mail_content = if mail.text_content.present?
mail.text_content[:reply]
elsif mail.html_content.present?
mail.html_content[:reply]
end
attachments = mail.attachments.last(Message::NUMBER_OF_PERMITTED_ATTACHMENTS)
inline_attachments = attachments.select { |attachment| attachment[:original].inline? && mail_content.present? }
regular_attachments = attachments - inline_attachments
regular_attachments.each do |mail_attachment|
attachment = message.attachments.new(
account_id: conversation.account_id,
file_type: 'file'
)
attachment.file.attach(mail_attachment[:blob])
end
message.save!
end
```
</details>
When moving form using Gmail Legacy auth to using OAuth, we need the
email address that will be used to connect. This is because we need to
store this email address in the cache and reuse when we get the callback
to find the associated inbox.
However there are cases where the imap login might be
`support@company.com` and the email used to communicate will be
`contact@company.com` (Probably an alias) In that case, we need to send
the correct email address to Chatwoot when re-authenticating
At the moment, we used the inbox email. This PR adds a check that
defaults to to `imap_login` if that is available and imap is enabled
This PR also fixes an unrelated problem where the email inbox creation
flow was not working
---
Tested it, it is working correctly
