Instagram external echo messages were being saved with status:
delivered, but the message meta UI did not treat Instagram as a channel
eligible for delivered-state rendering. As a result, these messages fell
back to progress and showed as “Sending”. This change updates the
message status mapping in the new message UI to include Instagram in the
delivered-state condition.
## Description
Handle messages with null content properly in UI and email notifications
## Type of change
- [ ] Bug fix (non-breaking change which fixes an issue)
## Relevant Screenshots:
<img width="688" height="765" alt="Screenshot 2026-01-21 at 4 43 00 PM"
src="https://github.com/user-attachments/assets/6a27c22e-2ae6-4377-a05d-cfa44bf181fe"
/>
## 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
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Touches notification email templates and message rendering conditions;
mistakes could lead to missing content/attachments in emails or
incorrect UI visibility, but changes are localized and non-auth/security
related.
>
> **Overview**
> Agent notification emails for *assigned* and *participating* new
messages now include the actual message details (sender name, rendered
text when present, and attachment links) and gracefully fall back when
content is unavailable.
>
> To support this, the mailer now passes `@message` into Liquid via
`MessageDrop` (adding `attachments` URLs), and the dashboard message UI
now renders failed/external-error messages even when `content` is `null`
while tightening retry eligibility to require content or attachments
(and still within 1 day).
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
475c8cedda54eb5e806990f977faf8098d0b27d8. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
## Summary
Fix hardcoded `Chatwoot` branding in two UI tooltips using the existing
`useBranding` flow so self-hosted/white-label deployments no longer show
the wrong brand text.
## Changes
- LabelSuggestion tooltip now uses:
- `replaceInstallationName($t('LABEL_MGMT.SUGGESTIONS.POWERED_BY'))`
- Message avatar tooltip (native app/external echo) now uses:
- `replaceInstallationName(t('CONVERSATION.NATIVE_APP_ADVISORY'))`
## Why
This follows the existing branding pattern already used in the product
and keeps behavior consistent across deployments.
## Notes
- No change to message logic or API behavior.
- `AGENTS.md` updated with a branding guidance note.
## Fixes
- Fixes https://github.com/chatwoot/chatwoot/issues/13306
- Fixes https://github.com/chatwoot/chatwoot/issues/13466
## Testing
<img width="195" height="155" alt="Screenshot 2026-02-13 at 3 55 39 PM"
src="https://github.com/user-attachments/assets/5b295cdd-6e5d-42c0-bbd7-23ba7052e1c3"
/>
<img width="721" height="152" alt="Screenshot 2026-02-13 at 3 55 48 PM"
src="https://github.com/user-attachments/assets/19cec2a0-451f-4fb3-bd61-7c2e591fc3c7"
/>
When businesses use WhatsApp Business App (co-existence mode) or
Instagram App or TikTok alongside Chatwoot, messages sent from the
native apps were not synced properly back to Chatwoot. This left agents
with an incomplete conversation history and no visibility into responses
sent outside the dashboard. Additionally, if these echo messages did
arrive, they appeared as "Sent by: Bot" in the UI since they had no
sender, making it confusing for agents.
This PR subscribes to WhatsApp `smb_message_echoes` webhook events and
routes them through the existing service with an `outgoing_echo` flag,
mirroring how Instagram already handles echoes. On the Instagram side,
echo messages now also carry the `external_echo` content attribute and
`delivered` status.
On the frontend, messages with `externalEcho` are distinguished from bot
messages showing a "Native app" avatar and an advisory note encouraging
agents to reply from Chatwoot to maintain the service window.
<img width="1518" height="524" alt="CleanShot 2026-01-29 at 13 37 57@2x"
src="https://github.com/user-attachments/assets/5aa0b552-6382-441f-96aa-9a62ca716e4a"
/>
Fixes
https://linear.app/chatwoot/issue/CW-4204/display-messages-not-sent-from-chatwoot-in-case-of-outgoing-echo
Fixes
https://linear.app/chatwoot/issue/PLA-33/incoming-from-me-messages-from-whatsapp-business-app-are-not-falling
# Pull Request Template
## Description
This PR includes the following updates:
1. Updated the design system color tokens by introducing new tokens for
surfaces, overlays, buttons, labels, and cards, along with refinements
to existing shades.
2. Refreshed both light and dark themes with adjusted background,
border, and solid colors.
3. Replaced static Inter font files with the Inter variable font
(including italic), supporting weights from 100–900.
4. Added custom font weights (420, 440, 460, 520) along with custom
typography classes to enable more fine-grained and consistent typography
control.
## Type of change
- [x] New feature (non-breaking change which adds functionality)
## 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 <pranav@chatwoot.com>
Previously, translations were generated and resolved purely based on the
account locale. This caused issues in multi-team, multi-region setups
where agents often work in different languages than the account default.
For example, an account might be set to English, while an agent prefers
Spanish. In this setup:
- Translations were always created using the account locale.
- Agents could not view content in their preferred language.
- This did not scale well for global teams.
There was also an issue with locale resolution during rendering, where
the system would incorrectly default to the account locale even when a
more appropriate locale should have been used.
With this update, During rendering, the system first attempts to use the
agent’s locale. If a translation for that locale does not exist, it
falls back to the account locale.
**How to test:**
- Set agent locale to a specific language (e.g., zh_CN) and account
language to en.
- Translate a message.
- Verify translated content displays correctly for the agent's selected
locale
- Do the same for another locale for agent.
- With multiple translations on a message (e.g., zh_CN, es, ml), verify
the UI shows the one matching agent's locale
- Change agent locale and verify the displayed translation updates
accordingly
## Description
Implements comprehensive search functionality with advanced filtering
capabilities for Chatwoot (Linear: CW-5956).
This PR adds:
1. **Time-based filtering** for contacts and conversations (SQL-based
search)
2. **Advanced message search** with multiple filters
(OpenSearch/Elasticsearch-based)
- **`from` filter**: Filter messages by sender (format: `contact:42` or
`agent:5`)
- **`inbox_id` filter**: Filter messages by specific inbox
- **Time range filters**: Filter messages using `since` and `until`
parameters (Unix timestamps in seconds)
- **90-day limit enforcement**: Automatically limits searches to the
last 90 days to prevent performance issues
The implementation extends the existing `Enterprise::SearchService`
module for advanced features and adds time filtering to the base
`SearchService` for SQL-based searches.
## API Documentation
### Base URL
All search endpoints follow this pattern:
```
GET /api/v1/accounts/{account_id}/search/{resource}
```
### Authentication
All requests require authentication headers:
```
api_access_token: YOUR_ACCESS_TOKEN
```
---
## 1. Search All Resources
**Endpoint:** `GET /api/v1/accounts/{account_id}/search`
Returns results from all searchable resources (contacts, conversations,
messages, articles).
### Parameters
| Parameter | Type | Description | Required |
|-----------|------|-------------|----------|
| `q` | string | Search query | Yes |
| `page` | integer | Page number (15 items per page) | No |
| `since` | integer | Unix timestamp (contacts/conversations only) | No
|
| `until` | integer | Unix timestamp (contacts/conversations only) | No
|
### Example Request
```bash
curl -X GET "https://app.chatwoot.com/api/v1/accounts/1/search?q=customer" \
-H "api_access_token: YOUR_ACCESS_TOKEN"
```
### Example Response
```json
{
"payload": {
"contacts": [...],
"conversations": [...],
"messages": [...],
"articles": [...]
}
}
```
---
## 2. Search Contacts
**Endpoint:** `GET /api/v1/accounts/{account_id}/search/contacts`
Search contacts by name, email, phone number, or identifier with
optional time filtering.
### Parameters
| Parameter | Type | Description | Required |
|-----------|------|-------------|----------|
| `q` | string | Search query | Yes |
| `page` | integer | Page number (15 items per page) | No |
| `since` | integer | Unix timestamp - filter by last_activity_at | No |
| `until` | integer | Unix timestamp - filter by last_activity_at | No |
### Example Requests
**Basic search:**
```bash
curl -X GET "https://app.chatwoot.com/api/v1/accounts/1/search/contacts?q=john" \
-H "api_access_token: YOUR_ACCESS_TOKEN"
```
**Search contacts active in the last 7 days:**
```bash
SINCE=$(date -v-7d +%s)
curl -X GET "https://app.chatwoot.com/api/v1/accounts/1/search/contacts?q=john&since=${SINCE}" \
-H "api_access_token: YOUR_ACCESS_TOKEN"
```
**Search contacts active between 30 and 7 days ago:**
```bash
SINCE=$(date -v-30d +%s)
UNTIL=$(date -v-7d +%s)
curl -X GET "https://app.chatwoot.com/api/v1/accounts/1/search/contacts?q=john&since=${SINCE}&until=${UNTIL}" \
-H "api_access_token: YOUR_ACCESS_TOKEN"
```
### Example Response
```json
{
"payload": {
"contacts": [
{
"id": 42,
"email": "john@example.com",
"name": "John Doe",
"phone_number": "+1234567890",
"identifier": "user_123",
"additional_attributes": {},
"created_at": 1701234567
}
]
}
}
```
---
## 3. Search Conversations
**Endpoint:** `GET /api/v1/accounts/{account_id}/search/conversations`
Search conversations by display ID, contact name, email, phone number,
or identifier with optional time filtering.
### Parameters
| Parameter | Type | Description | Required |
|-----------|------|-------------|----------|
| `q` | string | Search query | Yes |
| `page` | integer | Page number (15 items per page) | No |
| `since` | integer | Unix timestamp - filter by last_activity_at | No |
| `until` | integer | Unix timestamp - filter by last_activity_at | No |
### Example Requests
**Basic search:**
```bash
curl -X GET "https://app.chatwoot.com/api/v1/accounts/1/search/conversations?q=billing" \
-H "api_access_token: YOUR_ACCESS_TOKEN"
```
**Search conversations active in the last 24 hours:**
```bash
SINCE=$(date -v-1d +%s)
curl -X GET "https://app.chatwoot.com/api/v1/accounts/1/search/conversations?q=billing&since=${SINCE}" \
-H "api_access_token: YOUR_ACCESS_TOKEN"
```
**Search conversations from last month:**
```bash
SINCE=$(date -v-30d +%s)
UNTIL=$(date +%s)
curl -X GET "https://app.chatwoot.com/api/v1/accounts/1/search/conversations?q=billing&since=${SINCE}&until=${UNTIL}" \
-H "api_access_token: YOUR_ACCESS_TOKEN"
```
### Example Response
```json
{
"payload": {
"conversations": [
{
"id": 123,
"display_id": 45,
"inbox_id": 1,
"status": "open",
"messages": [...],
"meta": {...}
}
]
}
}
```
---
## 4. Search Messages (Advanced)
**Endpoint:** `GET /api/v1/accounts/{account_id}/search/messages`
Advanced message search with multiple filters powered by
OpenSearch/Elasticsearch.
### Prerequisites
- OpenSearch/Elasticsearch must be running (`OPENSEARCH_URL` env var
configured)
- Account must have `advanced_search` feature flag enabled
- Messages must be indexed in OpenSearch
### Parameters
| Parameter | Type | Description | Required |
|-----------|------|-------------|----------|
| `q` | string | Search query | Yes |
| `page` | integer | Page number (15 items per page) | No |
| `from` | string | Filter by sender: `contact:{id}` or `agent:{id}` |
No |
| `inbox_id` | integer | Filter by specific inbox ID | No |
| `since` | integer | Unix timestamp - searches from this time (max 90
days ago) | No |
| `until` | integer | Unix timestamp - searches until this time | No |
### Important Notes
- **90-Day Limit**: If `since` is not provided, searches default to the
last 90 days
- If `since` exceeds 90 days, returns `422` error: "Search is limited to
the last 90 days"
- All time filters use message `created_at` timestamp
### Example Requests
**Basic message search:**
```bash
curl -X GET "https://app.chatwoot.com/api/v1/accounts/1/search/messages?q=refund" \
-H "api_access_token: YOUR_ACCESS_TOKEN"
```
**Search messages from a specific contact:**
```bash
curl -X GET "https://app.chatwoot.com/api/v1/accounts/1/search/messages?q=refund&from=contact:42" \
-H "api_access_token: YOUR_ACCESS_TOKEN"
```
**Search messages from a specific agent:**
```bash
curl -X GET "https://app.chatwoot.com/api/v1/accounts/1/search/messages?q=refund&from=agent:5" \
-H "api_access_token: YOUR_ACCESS_TOKEN"
```
**Search messages in a specific inbox:**
```bash
curl -X GET "https://app.chatwoot.com/api/v1/accounts/1/search/messages?q=refund&inbox_id=3" \
-H "api_access_token: YOUR_ACCESS_TOKEN"
```
**Search messages from the last 7 days:**
```bash
SINCE=$(date -v-7d +%s)
curl -X GET "https://app.chatwoot.com/api/v1/accounts/1/search/messages?q=refund&since=${SINCE}" \
-H "api_access_token: YOUR_ACCESS_TOKEN"
```
**Search messages between specific dates:**
```bash
SINCE=$(date -v-30d +%s)
UNTIL=$(date -v-7d +%s)
curl -X GET "https://app.chatwoot.com/api/v1/accounts/1/search/messages?q=refund&since=${SINCE}&until=${UNTIL}" \
-H "api_access_token: YOUR_ACCESS_TOKEN"
```
**Combine all filters:**
```bash
SINCE=$(date -v-14d +%s)
curl -X GET "https://app.chatwoot.com/api/v1/accounts/1/search/messages?q=refund&from=contact:42&inbox_id=3&since=${SINCE}" \
-H "api_access_token: YOUR_ACCESS_TOKEN"
```
**Attempt to search beyond 90 days (returns error):**
```bash
SINCE=$(date -v-120d +%s)
curl -X GET "https://app.chatwoot.com/api/v1/accounts/1/search/messages?q=refund&since=${SINCE}" \
-H "api_access_token: YOUR_ACCESS_TOKEN"
```
### Example Response (Success)
```json
{
"payload": {
"messages": [
{
"id": 789,
"content": "I need a refund for my purchase",
"message_type": "incoming",
"created_at": 1701234567,
"conversation_id": 123,
"inbox_id": 3,
"sender": {
"id": 42,
"type": "contact"
}
}
]
}
}
```
### Example Response (90-day limit exceeded)
```json
{
"error": "Search is limited to the last 90 days"
}
```
**Status Code:** `422 Unprocessable Entity`
---
## 5. Search Articles
**Endpoint:** `GET /api/v1/accounts/{account_id}/search/articles`
Search help center articles by title or content.
### Parameters
| Parameter | Type | Description | Required |
|-----------|------|-------------|----------|
| `q` | string | Search query | Yes |
| `page` | integer | Page number (15 items per page) | No |
### Example Request
```bash
curl -X GET "https://app.chatwoot.com/api/v1/accounts/1/search/articles?q=installation" \
-H "api_access_token: YOUR_ACCESS_TOKEN"
```
### Example Response
```json
{
"payload": {
"articles": [
{
"id": 456,
"title": "Installation Guide",
"slug": "installation-guide",
"portal_slug": "help",
"account_id": 1,
"category_name": "Getting Started",
"status": "published",
"updated_at": 1701234567
}
]
}
}
```
---
## Technical Implementation
### SQL-Based Search (Contacts, Conversations, Articles)
- Uses PostgreSQL `ILIKE` queries by default
- Optional GIN index support via `search_with_gin` feature flag for
better performance
- Time filtering uses `last_activity_at` for contacts/conversations
- Returns paginated results (15 per page)
### Advanced Search (Messages)
- Powered by OpenSearch/Elasticsearch via Searchkick gem
- Requires `OPENSEARCH_URL` environment variable
- Requires `advanced_search` account feature flag
- Enforces 90-day lookback limit via
`Limits::MESSAGE_SEARCH_TIME_RANGE_LIMIT_DAYS`
- Validates inbox access permissions before filtering
- Returns paginated results (15 per page)
---
## Type of change
- [x] New feature (non-breaking change which adds functionality)
- [x] Enhancement (improves existing functionality)
---
## How Has This Been Tested?
### Unit Tests
- **Contact Search Tests**: 3 new test cases for time filtering
(`since`, `until`, combined)
- **Conversation Search Tests**: 3 new test cases for time filtering
- **Message Search Tests**: 10+ test cases covering:
- Individual filters (`from`, `inbox_id`, time range)
- Combined filters
- Permission validation for inbox access
- Feature flag checks
- 90-day limit enforcement
- Error handling for exceeded time limits
### Test Commands
```bash
# Run all search controller tests
bundle exec rspec spec/controllers/api/v1/accounts/search_controller_spec.rb
# Run search service tests (includes enterprise specs)
bundle exec rspec spec/services/search_service_spec.rb
```
### Manual Testing Setup
A rake task is provided to create 50,000 test messages across multiple
inboxes:
```bash
# 1. Create test data
bundle exec rake search:setup_test_data
# 2. Start OpenSearch
mise elasticsearch-start
# 3. Reindex messages
rails runner "Message.search_index.import Message.all"
# 4. Enable feature flag
rails runner "Account.first.enable_features('advanced_search')"
# 5. Test via API or Rails console
```
---
## 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
- [x] I have made corresponding changes to the documentation (this PR
description)
- [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
---
## Additional Notes
### Requirements
- **OpenSearch/Elasticsearch**: Required for advanced message search
- Set `OPENSEARCH_URL` environment variable
- Example: `export OPENSEARCH_URL=http://localhost:9200`
- **Feature Flags**:
- `advanced_search`: Account-level flag for message advanced search
- `search_with_gin` (optional): Account-level flag for GIN-based SQL
search
### Performance Considerations
- 90-day limit prevents expensive long-range queries on large datasets
- GIN indexes recommended for high-volume search on SQL-based resources
- OpenSearch/Elasticsearch provides faster full-text search for messages
### Breaking Changes
- None. All new parameters are optional and backward compatible.
### Frontend Integration
- Frontend PR tracking advanced search UI will consume these endpoints
- Time range pickers should convert JavaScript `Date` to Unix timestamps
(seconds)
- Date conversion: `Math.floor(date.getTime() / 1000)`
### Error Handling
- Invalid `from` parameter format is silently ignored (filter not
applied)
- Time range exceeding 90 days returns `422` with error message
- Missing `q` parameter returns `422` (existing behavior)
- Unauthorized inbox access is filtered out (no error, just excluded
from results)
---------
Co-authored-by: iamsivin <iamsivin@gmail.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: Pranav <pranav@chatwoot.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
fixes: #11834
This pull request introduces TikTok channel integration, enabling users
to connect and manage TikTok business accounts similarly to other
supported social channels. The changes span backend API endpoints,
authentication helpers, webhook handling, configuration, and frontend
components to support TikTok as a first-class channel.
**Key Notes**
* This integration is only compatible with TikTok Business Accounts
* Special permissions are required to access the TikTok [Business
Messaging
API](https://business-api.tiktok.com/portal/docs?id=1832183871604753).
* The Business Messaging API is region-restricted and is currently
unavailable to users in the EU.
* Only TEXT, IMAGE, and POST_SHARE messages are currently supported due
to limitations in the TikTok Business Messaging API
* A message will be successfully sent only if it contains text alone or
one image attachment. Messages with multiple attachments or those
combining text and attachments will fail and receive a descriptive error
status.
* Messages sent directly from the TikTok App will be synced into the
system
* Initiating a new conversation from the system is not permitted due to
limitations from the TikTok Business Messaging API.
**Backend: TikTok Channel Integration**
* Added `Api::V1::Accounts::Tiktok::AuthorizationsController` to handle
TikTok OAuth authorization initiation, returning the TikTok
authorization URL.
* Implemented `Tiktok::CallbacksController` to handle TikTok OAuth
callback, process authorization results, create or update channel/inbox,
and handle errors or denied scopes.
* Added `Webhooks::TiktokController` to receive and verify TikTok
webhook events, including signature verification and event dispatching.
* Created `Tiktok::IntegrationHelper` module for JWT-based token
generation and verification for secure TikTok OAuth state management.
**Configuration and Feature Flags**
* Added TikTok app credentials (`TIKTOK_APP_ID`, `TIKTOK_APP_SECRET`) to
allowed configs and app config, and registered TikTok as a feature in
the super admin features YAML.
[[1]](diffhunk://#diff-5e46e1d248631a1147521477d84a54f8ba6846ea21c61eca5f70042d960467f4R43)
[[2]](diffhunk://#diff-8bf37a019cab1dedea458c437bd93e34af1d6e22b1672b1d43ef6eaa4dcb7732R69)
[[3]](diffhunk://#diff-123164bea29f3c096b0d018702b090d5ae670760c729141bd4169a36f5f5c1caR74-R79)
**Frontend: TikTok Channel UI and Messaging Support**
* Added `TiktokChannel` API client for frontend TikTok authorization
requests.
* Updated channel icon mappings and tests to include TikTok
(`Channel::Tiktok`).
[[1]](diffhunk://#diff-b852739ed45def61218d581d0de1ba73f213f55570aa5eec52aaa08f380d0e16R16)
[[2]](diffhunk://#diff-3cd3ae32e94ef85f1f2c4435abf0775cc0614fb37ee25d97945cd51573ef199eR64-R69)
* Enabled TikTok as a supported channel in contact forms, channel
widgets, and feature toggles.
[[1]](diffhunk://#diff-ec59c85e1403aaed1a7de35971fe16b7033d5cd763be590903ebf8f1ca25a010R47)
[[2]](diffhunk://#diff-ec59c85e1403aaed1a7de35971fe16b7033d5cd763be590903ebf8f1ca25a010R69)
[[3]](diffhunk://#diff-725b90ca7e3a6837ec8291e9f57094f6a46b3ee00e598d16564f77f32cf354b0R26-R29)
[[4]](diffhunk://#diff-725b90ca7e3a6837ec8291e9f57094f6a46b3ee00e598d16564f77f32cf354b0R51-R54)
[[5]](diffhunk://#diff-725b90ca7e3a6837ec8291e9f57094f6a46b3ee00e598d16564f77f32cf354b0R68)
* Updated message meta logic to support TikTok-specific message statuses
(sent, delivered, read).
[[1]](diffhunk://#diff-e41239cf8dda36c1bd1066dbb17588ae8868e56289072c74b3a6d7ef5abdd696R23)
[[2]](diffhunk://#diff-e41239cf8dda36c1bd1066dbb17588ae8868e56289072c74b3a6d7ef5abdd696L63-R65)
[[3]](diffhunk://#diff-e41239cf8dda36c1bd1066dbb17588ae8868e56289072c74b3a6d7ef5abdd696L81-R84)
[[4]](diffhunk://#diff-e41239cf8dda36c1bd1066dbb17588ae8868e56289072c74b3a6d7ef5abdd696L103-R107)
* Added support for embedded message attachments (e.g., TikTok embeds)
with a new `EmbedBubble` component and updated message rendering logic.
[[1]](diffhunk://#diff-c3d701caf27d9c31e200c6143c11a11b9d8826f78aa2ce5aa107470e6fdb9d7fR31)
[[2]](diffhunk://#diff-047859f9368a46d6d20177df7d6d623768488ecc38a5b1e284f958fad49add68R1-R19)
[[3]](diffhunk://#diff-c3d701caf27d9c31e200c6143c11a11b9d8826f78aa2ce5aa107470e6fdb9d7fR316)
[[4]](diffhunk://#diff-cbc85e7c4c8d56f2a847d0b01cd48ef36e5f87b43023bff0520fdfc707283085R52)
* Adjusted reply policy and UI messaging for TikTok's 48-hour reply
window.
[[1]](diffhunk://#diff-0d691f6a983bd89502f91253ecf22e871314545d1e3d3b106fbfc76bf6d8e1c7R208-R210)
[[2]](diffhunk://#diff-0d691f6a983bd89502f91253ecf22e871314545d1e3d3b106fbfc76bf6d8e1c7R224-R226)
These changes collectively enable end-to-end TikTok channel support,
from configuration and OAuth flow to webhook processing and frontend
message handling.
------------
# TikTok App Setup & Configuration
1. Grant access to the Business Messaging API
([Documentation](https://business-api.tiktok.com/portal/docs?id=1832184145137922))
2. Set the app authorization redirect URL to
`https://FRONTEND_URL/tiktok/callback`
3. Update the installation config with TikTok App ID and Secret
4. Create a Business Messaging Webhook configuration and set the
callback url to `https://FRONTEND_URL/webhooks/tiktok`
([Documentation](https://business-api.tiktok.com/portal/docs?id=1832190670631937))
. You can do this by calling
`Tiktok::AuthClient.update_webhook_callback` from rails console once you
finish Tiktok channel configuration in super admin ( will be automated
in future )
5. Enable TikTok channel feature in an account
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
# load reply to message
## Description
When replayed message is more old, not show content
## Type of change
Please delete options that are not relevant.
- [X] New feature (non-breaking change which adds functionality)
## How Has This Been Tested?
I run in my development and production envinronment with unoapi
---------
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>
## Description
Introduces a global single-audio playback helper and hooks it into
dashboard and widget entrypoints. Adds play/pause event handlers in the
Audio chip to sync UI state. The helper enforces one audio playing at a
time and auto-advances to the next adjacent audio on end.
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
Fixes [CW-5540](https://linear.app/chatwoot/issue/CW-5540/feat-allow-retry-a-failed-message-sendout),
https://github.com/chatwoot/chatwoot/issues/12333
## Type of change
- [x] New feature (non-breaking change which adds functionality)
## How Has This Been Tested?
### Screenshot
<img width="196" height="113" alt="image"
src="https://github.com/user-attachments/assets/8b4a1aa9-c75c-4169-b4a2-e89148647e91"
/>
## 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: 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>
# Pull Request Template
## Description
Fixes#11761. See the issue for the details.
## Type of change
Please delete options that are not relevant.
- [x] 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?
I deployed a modified version of Chatwoot with this patch and tested.
## 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
- [x] I have made corresponding changes to the documentation (not
applicable)
- [x] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my
feature works (not sure how to do this)
- [x] New and existing unit tests pass locally with my changes
- [x] Any dependent changes have been merged and published in downstream
modules
---------
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
# Pull Request Template
## Description
Added input_select message type support for LINE
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
- [X] 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?
add testcase. and test manually by myself
## 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
- [ ] 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: Muhsin Keloth <muhsinkeramam@gmail.com>
# Pull Request
## Description
This PR fixes a UI issue where very long attachment filenames (sometimes
including parameters or hashes) would overflow or break the layout in
the message bubble. The fix applies Tailwind's truncate utility class to
ensure these filenames are properly truncated with ellipsis, preserving
layout consistency and improving readability.
Fixes#11514
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
- Manually tested with attachment messages containing long filenames
(e.g., with hashes, query params, or excessive length).
- Verified that filenames are now truncated with ellipsis.
## 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
- [x] New and existing unit tests pass locally with my changes
---------
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
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>
# Pull Request Template
## Description
This PR introduces basic customization options for the CSAT survey:
* **Display Type**: Option to use star ratings instead of emojis.
* **Message Text**: Customize the survey message (up to 200 characters).
* **Survey Rules**: Send surveys based on labels — trigger when a
conversation has or doesn't have a specific label.
Fixes
https://linear.app/chatwoot/document/improve-csat-responses-a61cf30e054e
## Type of change
- [x] New feature (non-breaking change which adds functionality)
## How Has This Been Tested?
### Loom videos
**Website Channel (Widget)**
https://www.loom.com/share/7f47836cde7940ae9d17b7997d060a18?sid=aad2ad0a-140a-4a09-8829-e01fa2e102c5
**Email Channel (Survey link)**
https://www.loom.com/share/e92f4c4c0f73417ba300a25885e093ce?sid=4bb006f0-1c2a-4352-a232-8bf684e3d757
## 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: Pranav <pranavrajs@gmail.com>
This PR fixes an issue where messages from the agent bot were incorrectly displayed as "BOT" with a missing avatar. It now correctly shows the agent bot’s name and avatar URL in the message list.
# Pull Request Template
## Description
This PR changes to translation to properly handle different content
types during translation.
### Changes
1. **Email translation with HTML support**
- Properly detects and preserves HTML content from emails
- Sets `mime_type` to 'text/html' when HTML content is present
2. **Email translation with plain text support**
- Falls back to email text content when HTML is not available
- Sets `mime_type` to 'text/plain' when HTML is not available and
content type includes 'text/plain'
3. **Plain message with plain text support (Non email channels)**
- Sets `mime_type` to 'text/plain' for non-email channels
- Fixes an issue where Markdown formatting was being lost due to
incorrect `mime_type`
**Note**: Translation for very long emails is not currently supported.
Fixes
https://linear.app/chatwoot/issue/CW-4244/translate-button-doesnt-work-in-email-channels
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
## How Has This Been Tested?
**Loom video**
https://www.loom.com/share/8f8428ed2cfe415ea5cb6c547c070f00?sid=eab9fa11-05f8-4838-9181-334bee1023c4
## 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
# Pull Request Template
## Description
This PR includes updating the PDF text color from Ruby to Slate for
improved readability
## How Has This Been Tested?
### Screenshots
**Before**
| Dark Mode | Light Mode |
| ------------- | ------------- |
| <img width="258" alt="image"
src="https://github.com/user-attachments/assets/6124c69d-8bf1-47f3-9a06-eeb21dbedbb2"
/> | <img width="258" alt="image"
src="https://github.com/user-attachments/assets/d0f2b90b-6796-45ae-a02a-05c07246d25c"
/> |
**After**
| Dark Mode | Light Mode |
| ------------- | ------------- |
| <img width="258" alt="image"
src="https://github.com/user-attachments/assets/c0285062-83b6-49d5-bdb0-2aebf977cf74"
/> | <img width="258" alt="image"
src="https://github.com/user-attachments/assets/6b153d45-ee8d-42ac-a863-0fa8b294a21b"
/> |
## 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
- [ ] 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
# Pull Request Template
## Description
This PR fixes the issue where a clock with animation is shown inside the
message bubble for failed and deleted messages. The message status is
now hidden for such messages.
## Type of change
Please delete options that are not relevant.
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
### Before
**Failed message bubble**
<img width="223" alt="image"
src="https://github.com/user-attachments/assets/bb4d7a34-4a1c-495a-9a3d-21d065bba020"
/>
**Deleted message bubble**
<img width="223" alt="image"
src="https://github.com/user-attachments/assets/ece8e2ff-c6d7-4fa7-b11c-04748bf9ea2d"
/>
### After
**Failed message bubble**
<img width="223" alt="image"
src="https://github.com/user-attachments/assets/6a6d81eb-52d9-48c3-bbc1-810b19770d61"
/>
**Deleted message bubble**
<img width="223" alt="image"
src="https://github.com/user-attachments/assets/828b553a-c88a-4a9e-9773-d75d76a9d0fd"
/>
## 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
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>
This pull request includes changes to various components adding a
`skip-context-menu` class to ensure the system context menu opens
instead of the Chatwoot message one
---------
Co-authored-by: Pranav <pranav@chatwoot.com>
Co-authored-by: Pranav <pranavrajs@gmail.com>
Previously, viewing translations required opening a modal, which was a
frustrating experience. This update improves the UX by displaying
translations inline.
https://www.loom.com/share/c69f4316248946208f2e56e2429063a2
A sample message with translated content (which has an option to see the
original):
<img width="313" alt="Screenshot 2025-03-12 at 6 57 49 PM"
src="https://github.com/user-attachments/assets/52434086-b57f-40e8-87b8-314c3519df4b"
/>
Right now, the translation is done based on the account locale. Some of
the future considerations.
- Add personal preference for the language (For eg: an agent can use the
software in spanish even though the company default is english), then
support translations based on personal preference.
- Add support for LLM translations (OpenAI integration)
- Add translations if Captain is enabled
- Add auto translation if the feature is turned on.
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>
# Pull Request Template
## Description
This PR includes fix for,
**1**. Incorrect error message translation for search in contact merge
modal.
<img width="395" alt="image"
src="https://github.com/user-attachments/assets/e6592bcc-4656-4a6e-83a9-7124e2c4732a"
/>
**2**. Z-index issue in chat screen
<img width="395" alt="image"
src="https://github.com/user-attachments/assets/387a2b47-ea85-49c7-832b-8bc1a4f0ec3f"
/>
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
**Screenshots**
**1.**
<img width="395" alt="image"
src="https://github.com/user-attachments/assets/a1f1e36d-0801-4e7b-9ee9-c72728933332"
/>
**2.**
<img width="395" alt="image"
src="https://github.com/user-attachments/assets/60a0ddc1-eb8f-4dbc-9028-245e6bb64c33"
/>
## 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
- [ ] 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