This PR adds the foundation for account-level SAML SSO configuration in
Chatwoot Enterprise. It introduces a new `AccountSamlSettings` model and
management API that allows accounts to configure their own SAML identity
providers independently, this also includes the certificate generation
flow
The implementation includes a new controller
(`Api::V1::Accounts::SamlSettingsController`) that provides CRUD
operations for SAML configuration
The feature is properly gated behind the 'saml' feature flag and
includes administrator-only authorization via Pundit policies.
## Linear reference:
https://linear.app/chatwoot/issue/CW-4649/re-imagine-assignments
## Description
This PR introduces the foundation for Assignment V2 system by
implementing agent_capacity and their association with inboxes and
users.
## Type of change
- [ ] New feature (non-breaking change which adds functionality)
## How Has This Been Tested?
Test Coverage:
- Controller specs for assignment policies CRUD operations
- Enterprise-specific specs for balanced assignment order
- Model specs for community/enterprise separation
## Checklist:
- [ ] My code follows the style guidelines of this project
- [ ] 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
- [ ] 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: Pranav <pranav@chatwoot.com>
This PR improves the voice call creation flow by simplifying
configuration and automating setup with Twilio APIs.
references: #11602 , #11481
## Key changes
- Removed the requirement for twiml_app_sid – provisioning is now
automated through APIs.
- Auto-configured webhook URLs for:
- Voice number callbacks
- Status callbacks
- twiML callbacks
- Disabled business hours, help center, and related options until voice
inbox is fully supported.
- Added a configuration tab in the voice inbox to display the required
Twilio URLs (to make verification easier in Twilio console).
## Test Cases
- Provisioning
- Create a new voice inbox → verify that Twilio app provisioning happens
automatically.
- Verify twiML callback
- Webhook configuration
- Check that both voice number callback and status callback URLs are
auto-populated in Twilio.
- Disabled features
- Confirm that business hours and help center options are
hidden/disabled for voice inbox.
- Configuration tab
- Open the voice inbox configuration tab → verify that the displayed
Twilio URLs match what’s set in Twilio.
## Linear reference:
https://linear.app/chatwoot/issue/CW-4649/re-imagine-assignments
## Description
This PR introduces the foundation for Assignment V2 system by
implementing assignment policies and their association with inboxes.
Assignment policies allow configuring how conversations are distributed
among agents, with support for different assignment orders (round_robin
in community, balanced in enterprise) and conversation prioritization
strategies
Fixes # (issue)
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality not to work as expected)
- [ ] This change requires a documentation update
## How Has This Been Tested?
Test Coverage:
- Controller specs for assignment policies CRUD operations
- Enterprise-specific specs for balanced assignment order
- Model specs for community/enterprise separation
Manual Testing:
1. Create assignment policy: POST
/api/v1/accounts/{id}/assignment_policies
2. List policies: GET /api/v1/accounts/{id}/assignment_policies
3. Assign policy to inbox: POST
/api/v1/accounts/{id}/assignment_policies/{id}/inboxes
4. View inbox policy: GET
/api/v1/accounts/{id}/inboxes/{id}/assignment_policy
5. Verify community edition ignores "balanced" assignment order
6. Verify enterprise edition supports both "round_robin" and "balanced"
- testing the flows after enterprise folder deletion
## Checklist:
- [ ] My code follows the style guidelines of this project
- [ ] 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
- [ ] 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: Pranav <pranavrajs@gmail.com>
Co-authored-by: Pranav <pranav@chatwoot.com>
WhatsApp templates without parameters (body-only templates like
notifications, confirmations) were failing to send with the error:
ArgumentError (Unknown legacy format: NilClass). This affected all
parameter-less templates across marketing messages, notifications, and
utility templates.
This PR fixes flaky test failures in the Instagram webhook specs that
were caused by Redis mutex lock conflicts when
tests ran in parallel.
### The Problem:
The InstagramEventsJob uses a Redis mutex with a key based on sender_id
and ig_account_id to prevent race
conditions. However, all test factories were using the same hardcoded
sender_id: 'Sender-id-1', causing multiple
test instances to compete for the same mutex lock when running in
parallel.
### The Solution:
- Updated all Instagram event factories to generate unique sender IDs
using SecureRandom.hex(4)
- Modified test stubs and expectations to work with dynamic sender IDs
instead of hardcoded values
- Ensured each test instance gets its own unique mutex key, eliminating
lock contention
# Pull Request Template
## Description
This PR adds support for WhatsApp campaigns to Chatwoot, allowing
businesses to reach their customers through WhatsApp. The implementation
includes backend support for WhatsApp template messages, frontend UI
components, and integration with the existing campaign system.
Fixes#8465
Fixes https://linear.app/chatwoot/issue/CW-3390/whatsapp-campaigns
## Type of change
- [x] New feature (non-breaking change which adds functionality)
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] Breaking change (fix or feature that would cause existing
functionality not to work as expected)
- [ ] This change requires a documentation update
## How Has This Been Tested?
- Tested WhatsApp campaign creation UI flow
- Verified backend API endpoints for campaign creation
- Tested campaign service integration with WhatsApp templates
- Validated proper filtering of WhatsApp campaigns in the store
## 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
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream
modules
## What we have changed:
We have added support for WhatsApp campaigns as requested in the
discussion.
Ref: https://github.com/orgs/chatwoot/discussions/8465
**Note:** This implementation doesn't exactly match the maintainer's
specification and variable support is missing. This is an initial
implementation that provides the core WhatsApp campaign functionality.
### Changes included:
**Backend:**
- Added `template_params` column to campaigns table (migration + schema)
- Created `Whatsapp::OneoffCampaignService` for WhatsApp campaign
execution
- Updated campaign model to support WhatsApp inbox types
- Added template_params support to campaign controller and API
**Frontend:**
- Added WhatsApp campaign page, dialog, and form components
- Updated campaign store to filter WhatsApp campaigns separately
- Added WhatsApp-specific routes and empty state
- Updated i18n translations for WhatsApp campaigns
- Modified sidebar to include WhatsApp campaigns navigation
This provides a foundation for WhatsApp campaigns that can be extended
with variable support and other enhancements in future iterations.
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This PR introduces a new channel type for voice conversations.
ref: #11481
## Changes
- Add database migration for channel_voice table with phone_number and
provider_config
- Create Channel::Voice model with E.164 phone number validation and
Twilio config validation
- Add voice channel association to Account model
- Extend inbox helpers and types to support voice channels
- Add voice channel setup UI with Twilio configuration form
- Include voice channel in channel factory and list components
- Add API routes and store actions for voice channel creation
- Add comprehensive translations for voice channel management
---------
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
## Summary
- support region option when configuring Dialogflow integration
- connect to region endpoint when set
- use session identification based on the region
Fixes: https://github.com/chatwoot/chatwoot/issues/4129
- Enable jobs by default when a copilot thread or a message is created.
- Rename thread_id to copilot_thread_id to keep it consistent with the
model name
- Add a spec for search_linear_issues service
- Add API support for creating a thread
- Add API support for creating a message
- Remove uuid from thread (no longer required, we will use existing
websocket connection to send messages)
- Update message_type to a column (user, assistant, assistant_thinking)
The agents can see the previous conversations with the copilot if needed
with this change. We would have to cleanup the data after a while. For
now, that is not considered.
This PR adds:
- A new model for copilot_threads (intentionally named thread instead of
conversation to avoid confusion), copilot_messages
- Add the controller to fetch previous threads and messages.
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>
- 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
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
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 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.
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.
- 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>
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>
Fixes https://github.com/chatwoot/chatwoot/issues/9935
Fixes https://github.com/chatwoot/chatwoot/issues/8213
The articles were grouped by category, with locale being a derived
attribute from the category. If a category was deleted, the article
wouldn't appear on the dashboard. However, due to a bug, it would show
up in the uncategorized section on the public portal, leaving agents
unable to edit or update the article.
To address this issue, I've added a locale attribute directly to the
article. This attribute is automatically set from the category or the
portal's default locale if not supplied. The API parameters now use this
attribute to filter articles. As a result, the dashboard will display
articles even if they're not associated with a category, improving the
overall workflow.
**Main updates:**
- Add locale attribute to the Article model. Add db migration to back
fill the data based on the above logic.
- Add a new scope search_by_locale and use it instead of
search_by_category_locale.
- Update the ERB template to include the locale filter.
- Move from `joins` to `left_outer_joins` to include the articles with
no categories.
---------
Co-authored-by: Sojan <sojan@pepalo.com>
- Previously we were ignoring the reels shared over Instagram messages.
This PR will render the reels with in Chatwoot.
followup : we need to render reels in a better interface so that it is
clearly denoted to the user that its an Instagram reel
Webpush gem throws errors such as `WebPush::ExpiredSubscription`,
`WebPush::InvalidSubscription`, `WebPush::Unauthorized`. We handled only
ExpiredSubscription.
If the SDK threw any other errors, it would pause sending the
notification to all other devices for that user. This change would
update the logic to remove the expired subscription and handle the rest
of the errors gracefully.
Fixes
https://linear.app/chatwoot/issue/CW-3399/webpushinvalidsubscription-host-fcmgoogleapiscom-nethttpnotfound-404
This change introduces the ability to lock conversations to a single thread for Instagram and facebook messages within the Meta inbox, mirroring existing functionality in WhatsApp and SMS inboxes.
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
- Added a trait called microsoft_email for the Channel::Email factory.
- Rewrote the logic to make it simple to understand
- Rewrote the specs for readability
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