Adds a Call model to track voice call state across providers (Twilio,
WhatsApp). This replaces storing call data in
conversation.additional_attributes and provides a foundation for call
analytics multi-call-per-conversation support, and future voice
providers.
---------
Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com>
## Description
ConversationReplyMailer#parse_email calls
Mail::Address.new(email_string).address without error handling. When an
account's support_email contains a non-email string (e.g., "Smith
Smith"), the mail gem raises Mail::Field::IncompleteParseError, crashing
conversation transcript emails.
This has caused 1,056 errors on Sentry (EXTERNAL-CHATINC-JX) since Feb
25, all from a single account that has a name stored in the
support_email field instead of a valid email address.
Closes
https://linear.app/chatwoot/issue/CW-6687/mailfieldincompleteparseerror-mailaddresslist-can-not-parse-orsmith
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
## 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: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Vishnu Narayanan <iamwishnu@gmail.com>
## Linear ticket
https://linear.app/chatwoot/issue/CW-6834/billing-upgrade-didnt-work
## Description
A `customer.subscription.updated` Stripe webhook for account 76162
returned 200 OK but did not persist the new `subscribed_quantity`. Root
cause: a race condition between the webhook handler and
`increment_response_usage` (Captain usage counter), both doing
read-modify-write on the `custom_attributes` JSONB column. The webhook
wrote `quantity: 6`, then a concurrent `save` from
`increment_response_usage` overwrote the entire hash with stale data —
restoring `quantity: 5`.
Fix: use atomic `jsonb_set` so usage counter updates only touch the
single key they care about, instead of rewriting the whole
`custom_attributes` hash. `increment_custom_attribute` also performs the
increment in SQL, making concurrent increments correct as well.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
- New regression spec in `handle_stripe_event_service_spec.rb` that
simulates concurrent webhook + `increment_response_usage` and asserts
both `subscribed_quantity` and `captain_responses_usage` survive
- Existing account, billing, captain, and topup specs all pass locally
## 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
Label attach/detach against a shared label no longer deadlocks under
parallel load. During high-concurrency label writes (for example, a
broadcast script attaching a campaign label to many conversations at
once), Chatwoot previously hit periodic `ActiveRecord::Deadlocked`
errors and tail-latency spikes on the tags table. This PR removes the
contention by disabling the `acts-as-taggable-on` counter cache, which
Chatwoot never reads.
## Closes
Fixes [INF-68](https://linear.app/chatwoot/issue/INF-68) (event 2)
## How to reproduce
1. Seed an account with ~20 conversations and 5 labels.
2. Spawn 20 parallel threads, each calling
`conversation.update!(label_list: shared_labels.shuffle)` against
different conversations.
3. Observe `ActiveRecord::Deadlocked` exceptions and p99 label-write
latency well above 1s.
With the counter cache disabled, the deadlock cycle cannot form.
## How this was tested
- Ran a 20-thread synthetic load test locally, each thread attaching 5
shared labels (shuffled per request) to different conversations. With
the counter cache enabled: 8 deadlocks across 300 attempts, p99 ~2.2s.
With the counter cache disabled: zero deadlocks, p99 ~306ms (roughly 85%
tail-latency reduction). The `UPDATE tags SET taggings_count = ...`
statement disappears from the SQL log entirely.
- Verified at boot via `rails runner` that
`ActsAsTaggableOn::Tagging.reflect_on_association(:tag).options[:counter_cache]`
returns `false` after the initializer runs. The gem wires `belongs_to
:tag, counter_cache: ActsAsTaggableOn.tags_counter` at class-load time,
so the initializer must sit ahead of the `Tagging` autoload path; this
confirms it does.
## Description
The IMAP email fetch job (Inboxes::FetchImapEmailsJob) crashes with an
unhandled IOError: closed stream when the mail server's SSL socket is
closed mid-write during Net::IMAP#fetch. This error was being reported
to Sentry because the rescue clause only caught EOFError, not its parent
class IOError.
Fixes
[CW-6689](https://linear.app/chatwoot/issue/CW-6689/ioerror-closed-stream-ioerror)
Widened the rescue in fetch_imap_emails_job.rb from EOFError to IOError.
In Ruby's exception hierarchy, EOFError is a subclass of IOError:
```
StandardError
└── IOError
└── EOFError
```
The Sentry stacktrace shows a plain IOError: closed stream raised from
OpenSSL::Buffering#do_write → Net::IMAP#put_string → Net::IMAP#fetch.
Since this is an IOError (not EOFError), it bypassed the existing rescue
and fell through to the StandardError catch-all, which reported it to
Sentry as an unhandled exception.
Rescuing IOError now catches both:
IOError: closed stream — the reported crash (parent class)
EOFError — the previously handled case (still caught as a subclass)
## Type of change
- [ ] Bug fix (non-breaking change which fixes an issue)
## 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: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Routing errors (404s) are expected in production and don't represent
actionable issues. Reporting them to New Relic creates noise and makes
it harder to spot real errors. Adds ActionController::RoutingError to
the New Relic error_collector.ignore_errors list so these are no longer
tracked as exceptions.
Removes sentry flooding of unnecessary rubyllm logs of wrong API key.
Logs only system api key error since it would be P0.
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## Description
Enable assignment v2 by default for new accounts
## 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 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
HandoffTool changes conversation status but only posts a private note.
ResponseBuilderJob now detects the tool flag and creates the public
handoff message that was previously only shown in V1.
# Pull Request Template
## Description
Captain V2 was silently forwarding conversations to humans without
showing a handoff message to the customer. The conversation appeared to
just stop
responding.
Root cause: In V2, HandoffTool calls bot_handoff! during agent
execution, which changes conversation status from pending to open. By
the time control returns
to ResponseBuilderJob#process_response, the conversation_pending? guard
returns early - skipping create_handoff_message entirely. The V1 flow
didn't have this
problem because AssistantChatService just returns a string token
(conversation_handoff) and lets ResponseBuilderJob handle everything.
What changed:
1. AgentRunnerService now surfaces the handoff_tool_called flag (already
tracked internally for usage metadata) in its response hash.
2. ResponseBuilderJob#handoff_requested? detects handoffs from both V1
(response token) and V2 (tool flag).
3. ResponseBuilderJob#process_response checks handoff_requested? before
the conversation_pending? guard, so V2 handoffs are processed even when
the status has
already changed.
4. ResponseBuilderJob#process_action('handoff') captures
conversation_pending? before calling bot_handoff! and uses that snapshot
to guard both bot_handoff!
and the OOO message - preventing double-execution when V2's HandoffTool
already ran them.
New V2 handoff flow:
AgentRunnerService
→ agent calls HandoffTool (creates private note, calls bot_handoff!)
→ returns response with handoff_tool_called: true
ResponseBuilderJob#process_response
→ handoff_requested? detects the flag
→ process_action('handoff')
→ create_handoff_message (public message for customer)
→ bot_handoff! skipped (conversation_pending? is false)
→ OOO skipped (conversation_pending? is false)
Fixes#13881
## 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?
- Update existing response_builder_job_spec.rb covering the V2 handoff
path, V2 normal response path, and V1 regression
- Updated existing agent_runner_service_spec.rb expectations for the new
handoff_tool_called key and added a context for when the flag is true
## 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
- [x] Any dependent changes have been merged and published in downstream
modules
---------
Co-authored-by: Aakash Bakhle <48802744+aakashb95@users.noreply.github.com>
Co-authored-by: aakashb95 <aakashbakhle@gmail.com>
Comprehensive update to Traditional Chinese (Taiwan) translations. As a
native zh-TW speaker and active user based in Taiwan, I found the
existing translations were quite incomplete (~54% overall) with many
strings still in English. Some existing translations also used
Simplified Chinese terms or unnatural phrasing.
I chose to submit this as a direct PR rather than going through Crowdin
because working through all the files at once is much faster and lets me
ensure consistent terminology across the entire locale.
Closes#14003
## What changed
**Backend (`config/locales/zh_TW.yml`)**
- Translated all ~259 previously untranslated strings (was ~19%
complete, now 100%)
- Covers: error messages, notifications, activity logs, integration
descriptions, Captain AI, public portal, reports
**Frontend (42 JSON files under `dashboard/i18n/locale/zh_TW/`)**
- Translated ~2,627 previously untranslated strings (was ~50% complete,
now ~100%)
- Most impacted files: `inboxMgmt.json`, `integrations.json`,
`settings.json`, `conversation.json`, `contact.json`, `report.json`
**Quality fixes across all files**
- Replaced Simplified Chinese terms mixed into zh-TW: 账→帳, 获→取得, 模板→範本,
收件箱→收件匣, 重置→重設, 自定義→自訂
- Standardized terminology for consistency: 客服人員 (agent), 延後 (snooze),
稽核 (audit), 巨集 (macro)
- Fixed incorrect translations (e.g., audit log table headers were
swapped, availability label was wrong)
## How to test
1. Set account/user language to 中文(台灣)
2. Navigate through the dashboard — settings, inbox management,
integrations, reports, conversations
3. Verify strings display in natural Traditional Chinese with no
remaining English gaps
4. Check that all placeholders (names, counts, dates) render correctly
# Pull Request Template
## Description
This PR includes, block inline images in message signatures and prevent
auto signature insertion when editor is disabled.
- Strip inline base64 images from signature on save and show warning
message
- Add `INLINE_IMAGE_WARNING` translation key for signature inline image
removal notification
- Add disabled check to `addSignature()` to prevent signature insertion
when editor is disabled
- Add `isEditorDisabled` checks to signature toggle logic in
`toggleSignatureForDraft()`, `replaceText()`, and `clearMessage()`
- Remove unused `replaceText` from the codebase, which belongs to old
`textarea` editor
Fixes
https://linear.app/chatwoot/issue/CW-6588/the-browser-hangs-when-the-message-signature-contains-inline-image
## 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/fb556b46a12a4308a737eed732d5ed73
## 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
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
## Account branding enrichment during signup
This PR does the following
### Replace Firecrawl with Context.dev
Switches the enterprise brand lookup from Firecrawl to Context.dev for
better data quality, built-in caching, and automatic filtering of
free/disposable email providers. The service interface changes from URL
to email input to match Context.dev's email endpoint. OSS still falls
back to basic HTML scraping with a normalized output shape across both
paths.
The enterprise path intentionally does not fall back to HTML scraping on
failure — speed matters more than completeness. We want the user on the
editable onboarding form fast, and a slow fallback scrape is worse than
letting them fill it in.
Requires `CONTEXT_DEV_API_KEY` in Super Admin → App Config. Without it,
falls back to OSS HTML scraping.
### Add job to enrich account details
After account creation, `Account::BrandingEnrichmentJob` looks up the
signup email and pre-fills the account name, colors, logos, social
links, and industry into `custom_attributes['brand_info']`.
The job signals completion via a short-lived Redis key (30s TTL) + an
ActionCable broadcast (`account.enrichment_completed`). The Redis key
lets the frontend distinguish "still running" from "finished with no
results."
Previously, signing up gave immediate access to the app. Now,
unconfirmed users are redirected to a verification page where they can
resend the confirmation email.
- After signup, the user is routed to `/auth/verify-email` instead of
the dashboard
- After login, unconfirmed users are redirected to the verification page
- The dashboard route guard catches unconfirmed users and redirects them
- `active_for_authentication?` is removed from the sessions controller
so unconfirmed users can authenticate — the frontend gates access
instead
- If the user visits the verification page after already confirming,
they're automatically redirected to the dashboard
- No session is issued until the user is verified
<details><summary>Demo</summary>
<p>
#### Fresh Signup
https://github.com/user-attachments/assets/abb735e5-7c8e-44a2-801c-96d9e4823e51
#### Google Fresh Signup
https://github.com/user-attachments/assets/ab9e389a-a604-4a9d-b492-219e6d94ee3f
#### Create new account from Dashboard
https://github.com/user-attachments/assets/c456690d-1946-4e0b-834b-ad8efcea8369
</p>
</details>
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
# Pull Request Template
## Description
Custom tools is now discoverable on all plans
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Before:
<img width="390" height="446" alt="CleanShot 2026-04-02 at 13 40 11@2x"
src="https://github.com/user-attachments/assets/0a751954-f3ad-47d6-85b8-1e2f1476a646"
/>
After:
<img width="392" height="522" alt="CleanShot 2026-04-02 at 13 40 47@2x"
src="https://github.com/user-attachments/assets/62a252f6-2551-47a9-b50c-be949f08c456"
/>
<img width="1826" height="638" alt="CleanShot 2026-04-02 at 13 37 39@2x"
src="https://github.com/user-attachments/assets/77dc2a75-3d76-44cf-8579-8d3457879bd0"
/>
## 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
- [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
- [x] Any dependent changes have been merged and published in downstream
modules
---------
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
Fixes failing `agent_bot_listener_spec.rb` tests for
`conversation_status_changed` events. After #13892 added webhook
signing, `process_webhook_bot_event` passes `:agent_bot_webhook` and
`secret:`/`delivery_id:` kwargs to
`AgentBots::WebhookJob.perform_later`, but two spec expectations were
not updated to match the new call signature.
## What changed
- Updated `perform_later` expectations in `conversation_status_changed`
specs to include the `:agent_bot_webhook` type and `secret` keyword
arguments.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Agent bots assigned to a conversation were not receiving
`conversation_status_changed` webhook events. This meant bots could not
react to status transitions like moving a conversation to **pending**.
The deprecated `conversation_opened` and `conversation_resolved` events
were still being delivered, but the newer unified
`conversation_status_changed` event was silently dropped because
`AgentBotListener` had no handler for it.
## What changed
- Added `conversation_status_changed` handler to `AgentBotListener`,
matching the pattern already used by `WebhookListener`. The payload
includes `changed_attributes` so bots know which status transition
occurred.
## How to test
1. Configure an agent bot with an `outgoing_url` (e.g. a webhook.site
endpoint).
2. Assign the bot to an inbox or conversation.
3. Change a conversation's status to **pending** (or any other status).
4. Verify the bot receives a `conversation_status_changed` event with
the correct `changed_attributes`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Account webhooks sign outgoing payloads with HMAC-SHA256, but agent bot
and API inbox webhooks were delivered unsigned. This PR adds the same
signing to both.
Each model gets a dedicated `secret` column rather than reusing the
agent bot's `access_token` (for API auth back into Chatwoot) or the API
inbox's `hmac_token` (for inbound contact identity verification). These
serve different trust boundaries and shouldn't be coupled — rotating a
signing secret shouldn't invalidate API access or contact verification.
The existing `Webhooks::Trigger` already signs when a secret is present,
so the backend change is just passing `secret:` through to the jobs.
Shared token logic is extracted into a `WebhookSecretable` concern
included by `Webhook`, `AgentBot`, and `Channel::Api`. The frontend
reuses the existing `AccessToken` component for secret display. Secrets
are admin-only and excluded from enterprise audit logs.
### How to test
Point an agent bot or API inbox webhook URL at a request inspector. Send
a message and verify `X-Chatwoot-Signature` and `X-Chatwoot-Timestamp`
headers are present. Reset the secret from settings and confirm
subsequent deliveries use the new value.
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
The `conversation_updated` webhook sent to AgentBots did not include
`changed_attributes`, making it impossible for bots to distinguish
between different types of conversation updates (e.g. bot assignment vs
label change vs status change).
This aligns the AgentBot webhook payload with the existing
`WebhookListener` behavior, which already includes `changed_attributes`.
## How to reproduce
1. Assign an AgentBot to a conversation
2. Then update the conversation (e.g. add a label)
3. **Before fix:** Both events arrive with identical payload structure —
bot cannot tell them apart
4. **After fix:** Each event includes `changed_attributes` showing
exactly what changed
## What changed
- **`AgentBotListener#conversation_updated`**: Added
`changed_attributes` to the webhook payload using
`extract_changed_attributes` (same pattern as `WebhookListener`)
## How to test
1. Assign an AgentBot to a conversation via API
2. Check the webhook payload — should include:
```json
"changed_attributes": [
{ "assignee_agent_bot_id": { "previous_value": null, "current_value": 7
} }
]
```
3. Update the conversation (e.g. add a label)
4. Check the webhook payload — `changed_attributes` should reflect the
label change, not bot assignment
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## Description
Two improvements to Agent Capacity Policy:
**1. Support exclusion via zero conversation limit**
Allow `conversation_limit` to be `0` on inbox capacity limits. Agents
with a zero limit are excluded from auto-assignment for that inbox while
remaining members for manual assignment.
**2. Fix exclusion rules duration input**
- Default changed from `10` to `null` so time-based exclusion isn't
applied unless explicitly set.
- Minimum lowered from 10 to 1 minute.
- `DurationInput` updated to handle `null` values correctly.
## 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)
## How Has This Been Tested?
- Added model and capacity service specs for zero-limit exclusion
behavior.
- Tested manually via UI flows
## 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: Muhsin Keloth <muhsinkeramam@gmail.com>
## Summary
When a Super Admin creates a new account via the Administrate dashboard,
the `manually_managed_features` field (a virtual attribute stored in
`internal_attributes` JSON) is passed to `Account.new(...)`, raising
`ActiveModel::UnknownAttributeError`. The existing `update` action
already strips this param — this fix adds the same handling to `create`.
Closes -> https://linear.app/chatwoot/issue/INF-66
Related Sentry ->
https://chatwoot-p3.sentry.io/issues/7168237533/?project=6382945&referrer=Linear
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How to reproduce
1. Log in as Super Admin
2. Navigate to Accounts → New
3. Fill in the form (with or without manually managed features selected)
4. Submit → `ActiveModel::UnknownAttributeError: unknown attribute
'manually_managed_features' for Account`
## What changed
- Added a `create` override in
`Enterprise::SuperAdmin::AccountsController` that strips
`manually_managed_features` from params before calling `super`, then
persists them via `InternalAttributesService` after the account is
saved.
Previously, all incoming messages from Facebook channel with
instagram_id had their attachment data_url and thumb_url overridden with
external_url. This caused issues for non-Instagram conversations
originating from Facebook Message where the file URL should be used
instead.
Narrows the override to only apply when the conversation type is
instagram_direct_message, which is the only case where Instagram's CDN
URLs need to be used directly.
Fixes
https://linear.app/chatwoot/issue/CW-6722/videos-are-missing-in-facebook-conversation
---------
Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When an AgentBot is assigned to a conversation after the first message
has already been received, the bot does not respond because it never
receives any event. The `message_created` event fires before the bot is
assigned, and the bot has no way to know it was assigned.
Chatwoot already dispatches a `CONVERSATION_UPDATED` event when
`assignee_agent_bot_id` changes, but `AgentBotListener` wasn't listening
for it. This fix adds a `conversation_updated` handler so the bot
receives a webhook with the conversation context when assigned.
## How to reproduce
1. Customer sends a message → conversation created, `message_created`
fires
2. System processes the message (adds labels, custom attributes)
3. System assigns an AgentBot to the conversation via API
4. **Before fix:** Bot receives no event and never responds
5. **After fix:** Bot receives `conversation_updated` event with
conversation payload
## What changed
- **`AgentBotListener`**: Added `conversation_updated` handler that
sends the conversation webhook payload to the assigned bot when the
conversation is updated
## How to test
1. Create an AgentBot with an `outgoing_url` pointing to a webhook
inspector (e.g. webhook.site)
2. Send a message to create a conversation
3. Assign the AgentBot to the conversation via API:
```
POST /api/v1/accounts/{id}/conversations/{id}/assignments
{ "assignee_id": <bot_id>, "assignee_type": "AgentBot" }
```
4. Verify the bot receives a `conversation_updated` event at its webhook
URL
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
### Description
When integrating the web widget via the JS SDK, customers call
setConversationCustomAttributes and setLabel on chatwoot:ready — before
any conversation exists. These API calls silently fail because the
backend endpoints require an existing conversation. When the visitor
sends their first message, the conversation is created without those
attributes/labels, so the message_created webhook payload is missing the
expected metadata.
This change queues SDK-set conversation custom attributes and labels in
the widget store when no conversation exists yet, and includes them in
the API request when the first message (or attachment) creates the
conversation. The backend now permits and applies these params during
conversation creation — before the message is saved and webhooks fire.
### How to test
1. Configure a web widget without a pre-chat form.
2. Open the widget on a test page and run the following in the browser
console after chatwoot:ready:
`window.$chatwoot.setConversationCustomAttributes({ plan: 'enterprise'
});`
`window.$chatwoot.setLabel('vip');` // must be a label that exists in
the account
3. Send the first message from the widget.
4. Verify in the Chatwoot dashboard that the conversation has plan:
enterprise in custom attributes and the vip label applied.
5. Set up a webhook subscriber for `message_created` confirm the first
payload includes the conversation metadata.
6. Verify that calling `setConversationCustomAttributes` / `setLabel` on
an existing conversation still works as before (direct API path, no
regression).
7. Verify the pre-chat form flow still works as expected.
Attachment webhook event payloads (`message_created`) were missing the
file extension and content type. The `extension` column existed but was
never populated, and `content_type` was not included in the payload at
all.
## What changed
- Added `before_save :set_extension` callback to extract file extension
from the filename when saving an attachment.
- Added `content_type` (from ActiveStorage) to the `file_metadata` used
in `push_event_data`.
### Before
```json
{
"extension": null,
"data_url": "...",
"file_size": 11960
}
```
### After
```json
{
"extension": "pdf",
"content_type": "application/pdf",
"data_url": "...",
"file_size": 11960
}
```
## How to reproduce
1. Send a message with a file attachment (e.g., PDF) via any channel
2. Inspect the `message_created` webhook payload
3. Observe `extension` is `null` and `content_type` is missing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
# Pull Request Template
## Description
Adds custom tool support to v1
## Type of change
- [x] New feature (non-breaking change which adds functionality)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
<img width="1816" height="958" alt="CleanShot 2026-03-24 at 11 37 33@2x"
src="https://github.com/user-attachments/assets/2777a953-8b65-4a2d-88ec-39f395b3fb47"
/>
<img width="378" height="488" alt="CleanShot 2026-03-24 at 11 38 18@2x"
src="https://github.com/user-attachments/assets/f6973c99-efd0-40e4-90fe-4472a2f63cea"
/>
<img width="1884" height="1452" alt="CleanShot 2026-03-24 at 11 38
32@2x"
src="https://github.com/user-attachments/assets/9fba4fc4-0c33-46da-888a-52ec6bad6130"
/>
## Checklist:
- [x] 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: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
When a user signs up with an email they don't own and sets a password,
that password remains valid even after the real owner later signs in via
OAuth. This means the original registrant — who never proved ownership
of the email — retains working credentials on the account. This change
closes that gap by rotating the password to a random value whenever an
unconfirmed user completes an OAuth sign-in.
The check (`oauth_user_needs_password_reset?`) is evaluated before
`skip_confirmation!` runs, since confirmation would flip `confirmed_at`
and mask the condition. If the user was unconfirmed, the stored password
is replaced with a secure random string that satisfies the password
policy. This applies to both the web and mobile OAuth callback paths, as
well as the sign-up path where the password is rotated before the reset
token is generated.
Users who lose access to password-based login as a side effect can
recover through the standard "Forgot password" flow at any time. Since
they've already proven email ownership via OAuth, this is a low-friction
recovery path
# Pull Request Template
## Description
This PR fixes an issue where markdown tables were not rendering
correctly in the Help Center.
The issue was caused by a backslash `(\)` being appended after table row
separators `(|)`, which breaks the markdown table parsing.
The issue was introduced after recent editor changes made to preserve
new lines, which unintentionally affected how table markdown is parsed
and displayed.
### https://github.com/chatwoot/prosemirror-schema/pull/44
Fixes
https://linear.app/chatwoot/issue/CW-6714/markdown-tables-dont-render-properly-in-help-centre-preview
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
**Before**
```
| Type | What you provide |\
|--------------|-------------------------------|\
| None | No authentication |\
| Bearer Token | A token string |\
| Basic Auth | Username and password |\
| API Key | A custom header name and value|
```
**After**
```
| Type | What you provide |
|--------------|-------------------------------|
| None | No authentication |
| Bearer Token | A token string |
| Basic Auth | Username and password |
| API Key | A custom header name and value|
```
## 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
## Summary
- Add `MutexApplicationJob::LockAcquisitionError` to Sentry's
`excluded_exceptions`
- This error is expected control flow (mutex lock contention during
webhook processing), not a bug
- Generated ~131K Sentry events in March 2026, 100% from
`InstagramEventsJob`
Fixes https://linear.app/chatwoot/issue/INF-58
Images and videos sent from Chatwoot to LINE inboxes fail to display on
the LINE mobile app — users see expired markers, broken thumbnails, or
missing images. This happens because LINE mobile lazy-loads images
rather than downloading them immediately, and the ActiveStorage signed
URLs expire after 5 minutes.
Closes
https://linear.app/chatwoot/issue/CW-6696/line-messaging-with-image-or-video-may-not-show-when-client-inactive
## How to reproduce
1. Create a LINE inbox and start a chat from the LINE mobile app
2. Close the LINE mobile app
3. Send an image from Chatwoot to that chat
4. Wait 7-8 minutes (past the 5-minute URL expiration)
5. Open the LINE mobile app — the image is broken/expired
## What changed
- **`originalContentUrl`**: switched from `download_url` (signed, 5-min
expiry) to `file_url` (permanent redirect-based URL)
- **`previewImageUrl`**: switched to `thumb_url` (250px resized
thumbnail meeting LINE's 1MB/240x240 recommendation), with fallback to
`file_url` for non-image attachments like video
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Sojan Jose <sojan@pepalo.com>
## Linear Ticket
https://linear.app/chatwoot/issue/CW-6707/socketresolutionerror-failed-to-open-tcp-connection-to-permanentlyhttps://linear.app/chatwoot/issue/CW-6707/socketresolutionerror-failed-to-open-tcp-connection-to-permanently#comment-14e0f9ff
## Description
Browser push notifications fail with Socket::ResolutionError when the
push subscription endpoint's domain can't be resolved via DNS (e.g.,
defunct push service, transient DNS failure). This error wasn't handled
in handle_browser_push_error, so it fell through to the catch-all else
branch and got reported to Sentry on every notification attempt — 1,637
times in the last 7 days.
The dead subscription was never cleaned up or the error suppressed, so
every subsequent notification for the affected user triggered the same
Sentry alert.
Added Socket::ResolutionError to the existing transient network error
handler alongside Errno::ECONNRESET, Net::OpenTimeout, and
Net::ReadTimeout. The error is logged but not reported to Sentry, and
the subscription is kept intact in case it's a temporary DNS blip.
## Type of change
- [ ] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
- Verified that Socket::ResolutionError is a subclass of StandardError
and matches the when clause
## 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: Vishnu Narayanan <iamwishnu@gmail.com>
# Pull Request Template
## Description
This PR fixes the white background bleed visible in the widget, widget
article viewer and help center when dark mode is active.
**What was happening**
While scrolling, the `<body>` element retained a white background in
dark mode. This occurred because dark mode classes were only applied to
inner container elements, not the root.
**What changed**
* **Widget:** Updated the `useDarkMode` composable to sync the `dark`
class to `<html>` using `watchEffect`, allowing `<body>` to inherit dark
theme variables. Also added background styles to `html`, `body`, and
`#app` in `woot.scss`.
* **Help center portal:** Moved `bg-white dark:bg-slate-900` from
`<main>` to `<body>` in the portal layout so the entire page background
responds correctly to dark mode, including within the widget iframe.
* **ArticleViewer:** Replaced hardcoded `bg-white` with `bg-n-solid-1`
to ensure better theming.
Fixes
https://linear.app/chatwoot/issue/CW-6704/widget-body-colour-not-implemented
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
### Screencasts
### Before
**Widget**
https://github.com/user-attachments/assets/e0224ad1-81a6-440a-a824-e115fb806728
**Help center**
https://github.com/user-attachments/assets/40a8ded5-5360-474d-9ec5-fd23e037c845
### After
**Widget**
https://github.com/user-attachments/assets/dd37cc68-99fc-4d60-b2ae-cf41f9d4d38c
**Help center**
https://github.com/user-attachments/assets/bc998c4e-ef77-46fa-ac7f-4ea16d912ce3
## 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
The initial version of prompt deciding to resolve or hand-off to human
agents was too conservative especially in cases where a link or an
action was told to customer. If the customer didn't respond, Captain was
told to hand it off to the agent, but customer may actually have solved
the issue. If not, they can come back and continue the conversation.
Removed two lines about the same and now we should not see needless
handoffs.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
locally
## 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
- [x] Any dependent changes have been merged and published in downstream
modules
# Pull Request Template
## Description
This PR fixes
1. Messages being trimmed to the default 1024 limit in `trimContent`
method, instead of channel-specific limits for drafts and AI tasks.
2. Telegram messages are allowed up to 10,000 characters in config, but
the API supports only 4096, causing failures for oversized messages.
Fixes
https://linear.app/chatwoot/issue/CW-6694/captain-ai-rewrite-tasks-truncate-draft-to-1024-chars-trimcontenthttps://github.com/chatwoot/chatwoot/issues/13919
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
### Loom video
**Before**
https://www.loom.com/share/00e9d6b4d19247febf35dffa99da3805
**After**
https://www.loom.com/share/c4900e9effc345c79bcd8a5aa1ee277b
## 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
Repurpose the deprecated response_bot feature flag slot for
custom_tools.
Migration disables the flag on any accounts that had response_bot
enabled so the repurposed slot starts in its default-off state.
Pre-deploy: run the disable script on production using the old flag name
(response_bot) before deploying this migration.
Adds `WebsiteBrandingService` (OSS) with an Enterprise override using
Firecrawl v2 to extract branding and business data from a URL for
onboarding auto-fill.
OSS version uses HTTParty + Nokogiri to extract:
- Business name (og:site_name or title)
- Language (html lang)
- Favicon
- Social links from `<a>` tags
Enterprise version makes a single Firecrawl call to fetch:
- Structured JSON (name, language, industry via LLM)
- Branding (favicon, primary color)
- Page links
Falls back to OSS if Firecrawl is unavailable or fails.
Social handles (WhatsApp, Facebook, Instagram, Telegram, TikTok, LINE)
are parsed deterministically via a shared `SocialLinkParser`.
> We use links for socials, since the LLM extraction was unreliable,
mostly returned empty, and hallucinated in some rare scenarios
## How to test
```ruby
# OSS (no Firecrawl key needed)
WebsiteBrandingService.new('chatwoot.com').perform
# Enterprise (requires CAPTAIN_FIRECRAWL_API_KEY)
WebsiteBrandingService.new('notion.so').perform
WebsiteBrandingService.new('postman.com').perform
```
Verify the returned hash includes business_name, language,
industry_category, social_handles, and branding with
favicon/primary_color.
<img width="908" height="393" alt="image"
src="https://github.com/user-attachments/assets/e3696887-d366-485a-89a0-8e1a9698a788"
/>
## Description
When a customer downgrades from Enterprise to Business, they may retain
unused Stripe credit balance. During an AI credits topup,
Stripe::Invoice.finalize_invoice auto-applies that credit balance to the
invoice. If the credit balance fully covers the invoice amount, Stripe
marks it as paid immediately upon finalization. Calling
Stripe::Invoice.pay on an already-paid invoice throws an error, breaking
the topup flow.
This fix retrieves the invoice status after finalization and skips the
pay call if Stripe has already settled it via credits.
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Tested against Stripe test mode with the following scenarios:
- Full credit balance payment: Customer has enough Stripe credit balance
to cover the entire invoice. Invoice is marked paid after
finalize_invoice — pay is correctly skipped. Credits are fulfilled
successfully.
- Partial credit balance payment: Customer has some Stripe credit
balance but not enough to cover the full amount. Invoice remains open
after finalization — pay is called and charges the remaining amount to
the default payment method. Credits are fulfilled successfully.
- Zero credit balance (normal payment): Customer has no Stripe credit
balance. Invoice remains open after finalization — pay charges the full
amount. Credits are fulfilled successfully.
## 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
Adds Estonian to the settings language dropdown so accounts can select
the existing `et` translation from the UI.
Fixes: N/A
Closes: N/A
## Why
Estonian translation files already exist in the repo and in Crowdin, but
the settings dropdown is driven by `LANGUAGES_CONFIG`, where `et` was
missing.
## What this change does
- Adds `et` / `Eesti (et)` to `LANGUAGES_CONFIG`
- Makes Estonian available in the settings language selectors backed by
`enabledLanguages`
## Validation
- `ruby -c config/initializers/languages.rb`
- Opened the local UI at `/app/accounts/1/settings/general` and verified
`Eesti (et)` appears in the `Site language` dropdown
---------
Co-authored-by: Pranav <pranav@chatwoot.com>
## Description
When Assignment V2 is enabled, the V2 capacity policies
(AgentCapacityPolicy / InboxCapacityLimit) are not respected during
team-based assignment paths. The system falls back to the legacy V1
max_assignment_limit, and since V1 is deprecated and typically
unconfigured in V2 setups, agents receive unlimited assignments
regardless of their V2 capacity.
Root cause: Inbox class directly defined
member_ids_with_assignment_capacity, which shadowed the
Enterprise::InboxAgentAvailability module override in Ruby's method
resolution order (MRO). This made the V2 capacity check unreachable
(dead code) for any code path using member_ids_with_assignment_capacity.
## Type of change
- [ ] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
⏺ Before the fix
1. Enable assignment_v2 + advanced_assignment on account
2. Create AgentCapacityPolicy with InboxCapacityLimit = 1 for an inbox
3. Assign the policy to an agent (e.g., John)
4. Create 1 open conversation assigned to John (now at capacity)
5. Create a new unassigned conversation in the same inbox
6. Assign a team (containing John) to that conversation
7. Result: John gets assigned despite being at capacity
⏺ After the fix
Same steps 1–6.
7. Result: John is NOT assigned — conversation stays unassigned (no
agents with capacity available)
## 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
This ignores `CVE-2026-33658` in `bundler-audit` after validating that
Chatwoot's default and recommended storage setups do not use Active
Storage proxy mode.
Fixes: N/A
Closes: N/A
## Why
`CVE-2026-33658` is an Active Storage proxy-mode DoS issue triggered by
multi-range requests.
For Chatwoot, the default and recommended setups do not appear to route
file downloads through Rails proxy mode:
- `config/environments/production.rb` selects the Active Storage service
but does not opt into `rails_storage_proxy`
- `.env.example` defaults to `ACTIVE_STORAGE_SERVICE=local`
- Chatwoot's storage docs recommend local/cloud storage with optional
direct uploads to the storage provider
- existing specs expect redirect/disk-style Active Storage URLs rather
than proxy-mode URLs
Given that validation, ignoring this advisory is a smaller and more
accurate response than a framework-wide Rails upgrade.
## What this change does
- adds `.bundler-audit.yml`
- preserves the existing advisory ignore entries already used by
Chatwoot
- ignores `CVE-2026-33658`
- documents why the ignore is acceptable for Chatwoot's current defaults
- notes that this should be revisited if Chatwoot enables
`rails_storage_proxy` or other app-served Active Storage proxy routes
## Validation
- reviewed `config/environments/production.rb`
- reviewed `.env.example`
- reviewed Chatwoot storage docs:
https://developers.chatwoot.com/self-hosted/deployment/storage/s3-bucket
- reviewed Active Storage URL expectations in
`spec/controllers/slack_uploads_controller_spec.rb` and
`spec/services/line/send_on_line_service_spec.rb`
- ran `bundle exec bundle-audit check --no-update`
This change blocks Help Center access for default/Hacker-plan accounts
and closes the downgrade gap that could leave `help_center` enabled
after a subscription falls back to the default cloud plan.
Fixes: none
Closes: none
## Why
Default-plan accounts should not be able to access the Help Center, but
the downgrade fallback path only reset the plan name and did not
reconcile premium feature flags. That meant some accounts could keep
`help_center` enabled even after landing back on the Hacker/default
plan.
## What this change does
- blocks Help Center portal and article access for default/Hacker-plan
accounts
- reconciles premium feature flags when a subscription falls back to the
default cloud plan, so `help_center` is disabled immediately instead of
waiting for a later webhook
- preserves existing account `custom_attributes` during Stripe customer
recreation instead of overwriting them
- adds Enterprise coverage for the default-plan access checks on hosted
and custom-domain Help Center routes
- fixes the public access check to use the resolved portal object so
blocked requests return the intended response instead of raising an
error
## Validation
1. Create or use an account on the default/Hacker cloud plan with an
active portal.
2. Visit the portal home page and a published article on both the
Chatwoot-hosted URL and a configured custom domain.
3. Confirm the Help Center is blocked for that account.
4. Downgrade a paid account back to the default/Hacker plan through the
Stripe webhook flow.
5. Confirm `help_center` is disabled right after the downgrade fallback
is processed and the account can no longer access the Help Center.
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Sojan Jose <sojan@pepalo.com>
# Pull Request Template
## Description
This PR upgrades the ProseMirror editor and enables automatic URL
linkification on paste. Previously, URLs were only linkified after a
user input event (e.g., typing a space). With this change, URLs are now
linkified instantly when pasted.
Fixes
https://linear.app/chatwoot/issue/CW-6682/email-channel-links-are-not-working
### https://github.com/chatwoot/prosemirror-schema/pull/42
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
**Screencast**
**Before**
https://github.com/user-attachments/assets/d38725c9-a152-4c2c-8c33-3ee717f1628f
**After**
https://github.com/user-attachments/assets/9a69a0b6-93ee-421e-896b-5a4e01a167ba
## 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 link formatting issue on the backend by adding
`:autolink` to `ChatwootMarkdownRenderer#render_message`, ensuring all
URLs are converted to `<a>` tags.
Fixes
https://linear.app/chatwoot/issue/CW-6682/email-channel-links-are-not-working
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## 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
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
issue: https://github.com/chatwoot/chatwoot/issues/13921
Fix a typo in the Messages API operationId where `converation` was used
instead of `conversation`. This causes cspell errors when generating
client code with tools like Orval.
## What changed
- `swagger/paths/public/inboxes/messages/index.yml`: fixed operationId
from `list-all-converation-messages` to `list-all-conversation-messages`
- `swagger/swagger.json` and `swagger/tag_groups/client_swagger.json`:
regenerated to reflect the fix
## Note
If you are using a code generator like Orval against this swagger spec,
the generated function name will change from
`listAllConverationMessages` to `listAllConversationMessages`.
Self-hosted installations were incorrectly hitting the daily email rate
limit of 100, seeded from `installation_config`. Since self-hosted users
control their own infrastructure, email rate limiting should only apply
to Chatwoot Cloud.
Closes#13913
After a successful WhatsApp OAuth reauthorization, the health check runs
immediately and finds the phone number in a pending provisioning state
(`platform_type: NOT_APPLICABLE`). This incorrectly triggers
`prompt_reauthorization!`, re-setting the Redis disconnect flag and
sending a disconnect email — even though the reauth just succeeded.
The fix skips the health check during reauthorization flows. It still
runs for new channel creation.
Closes https://github.com/chatwoot/chatwoot/pull/12556
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How to reproduce
1. Have a WhatsApp channel with a phone number in pending provisioning
state (display name not yet approved by Meta)
2. Complete the OAuth reauthorization flow
3. Observe that the user receives a "success" response but immediately
gets a disconnect email
## What changed
- `Whatsapp::EmbeddedSignupService#perform` now skips
`check_channel_health_and_prompt_reauth` when `inbox_id` is present
(reauthorization flow)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
When syncing contacts to LeadSquared, the `Lead.CreateOrUpdate` API
defaults to searching by email. If a contact has no email (or a
different email) but a phone number matching an existing lead, the API
fails with `MXDuplicateEntryException` instead of finding and updating
the existing lead. This accounted for ~69% of all LeadSquared
integration errors, and cascaded into "Lead not found" failures when
posting transcript and conversation activities (~14% of errors).
## What changed
- `LeadClient#create_or_update_lead` now catches
`MXDuplicateEntryException` and retries the request once with
`SearchBy=Phone` appended to the body, telling the API to match on phone
number instead
- Once the retry succeeds, the returned lead ID is stored on the contact
(existing behavior), so all future events use the direct `update_lead`
path and never hit the duplicate error again
## How to reproduce
1. Create a lead in LeadSquared with phone number `+91-75076767676` and
email `a@example.com`
2. In Chatwoot, create a contact with the same phone number but a
different email (or no email)
3. Trigger a contact sync (via conversation creation or contact update)
4. Before fix: `MXDuplicateEntryException` error in logs, contact fails
to sync
5. After fix: retry with `SearchBy=Phone` finds and updates the existing
lead, stores the lead ID on the contact
# Pull Request Template
## Description
Please include a summary of the change and issue(s) fixed. Also, mention
relevant motivation, context, and any dependencies that this change
requires.
- Add language_code setting to Dialogflow integration configuration
- Support 'auto' mode to detect language from contact's
additional_attributes
- Fallback to 'en-US' when no language is configured or detected
- Include comprehensive language options (22 languages)
- Add tests for language code configuration scenarios
Fixes#3071
## 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?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
<img width="815" height="506" alt="Screenshot 2026-01-10 220410"
src="https://github.com/user-attachments/assets/26d2619c-ed42-4c9a-a41d-9fb07ef91a30"
/>
## 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
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
This pull request fixes the model annotation tooling due to previous
incomplete migration from `annotate` to `annotaterb` gem (#12600). It
also improves the handling of serialized values in the
`InstallationConfig` model by ensuring a default value is set,
simplifying the code, and removing a workaround for YAML
deserialization.
**Annotation tooling updates:**
* Added `.annotaterb.yml` to configure the `annotate_rb` gem with
project-specific options, centralizing annotation settings.
* Replaced the custom `auto_annotate_models.rake` task with the standard
rake task from `annotate_rb`, and added `lib/tasks/annotate_rb.rake` to
load annotation tasks in development environments.
[[1]](diffhunk://#diff-9450d2359e45f1db407b3871dde787a25d60bb721aed179a65ffd2692e95fb4bL1-L61)
[[2]](diffhunk://#diff-578cdfc7ad56637e42472ea891ea286dff8803d9a1750afdbfeafec164d9b8b2R1-R8)
**Model serialization improvements:**
* Updated the `InstallationConfig` model to set a default value for the
`serialized_value` attribute, ensuring it always has a hash with
indifferent access and removing the need for a deserialization
workaround in the `value` method.
[[1]](diffhunk://#diff-b4bdde42c1ad0f584073818bd43dbd865b1b3b50d4701b131979f900d7c68297L22-R22)
[[2]](diffhunk://#diff-b4bdde42c1ad0f584073818bd43dbd865b1b3b50d4701b131979f900d7c68297L36-L39)
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
Adds a Platform API endpoint that allows migrating existing Google and
Microsoft email channels (with OAuth credentials) into Chatwoot without
requiring end-users to re-authenticate. This enables customers who lack
Rails console access to programmatically migrate email channels from
legacy systems.
### How to test
1. Create a Platform App and grant it permissible access to a target
account
2. `POST /platform/api/v1/accounts/:account_id/email_channel_migrations`
with a payload like:
```json
{
"migrations": [
{
"email": "support@example.com",
"provider": "google",
"provider_config": {
"access_token": "...",
"refresh_token": "...",
"expires_on": "..."
},
"inbox_name": "Migrated Support"
}
]
}
```
3. Verify channels are created with correct provider, provider_config, and IMAP defaults
4. Verify partial failures (e.g. duplicate email) don't roll back other migrations in the batch
5. Verify unauthenticated and non-permissible requests return 401
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
Webhook payloads (`message_created`, `message_updated`) started sending
channel-rendered HTML in the `content` field instead of the original raw
message content after PR #12878. This broke downstream agent bots and
integrations that expected plain text or markdown.
Closes https://linear.app/chatwoot/issue/PLA-109/webhook-payloads-send-channel-rendered-html-instead-of-raw-content
## How to reproduce
1. Connect an agent bot to a WebWidget inbox
2. Send a message with markdown formatting (e.g. `**bold**`) from the
widget
3. Observe the agent bot webhook payload — `content` field contains
`<p><strong>bold</strong></p>` instead of `**bold**`
## What changed
Split `MessageContentPresenter` into two public methods:
- `outgoing_content` — renders markdown for the target channel (used by
channel delivery services)
- `webhook_content` — returns raw content with CSAT survey URL when
applicable, no markdown rendering (used by `webhook_data`)
Updated `Message#webhook_data` to use `webhook_content` instead of
`outgoing_content`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
## Summary
- Adds `LimitNOFILE=65536` to both web and worker systemd service units
- Fixes recurring `Errno::EMFILE` (Too many open files) errors during
peak traffic after deploys
## Context
Puma workers idle at 720-770 open FDs against the default soft limit of
1024, leaving ~250 FDs of headroom. During deploy-triggered instance
refreshes at peak traffic, concurrent requests exhaust the remaining
FDs, causing EMFILE across all web instances.
3 incidents in March 2026 with escalating event counts. The hard limit
is already 524288, so this just raises the soft limit to a standard
production value.
Self-hosted instances pick this up automatically via `cwctl --upgrade`.
Fixes
https://linear.app/chatwoot/issue/CW-6685/errnoemfile-too-many-open-files-rb-sysopen
# Pull Request Template
## Description
Relocate controller from enterprise/ to app/ and add
Api::V1::Accounts::Captain::TasksController.prepend_mod_with for EE
overrides.
Fixes: Ai assist giving 404 on CE
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
before:
<img width="482" height="130" alt="image"
src="https://github.com/user-attachments/assets/f51dc28a-ac54-45c4-9015-6f956fdf5057"
/>
after:
<img width="458" height="182" alt="image"
src="https://github.com/user-attachments/assets/eb86a679-5482-4157-9f4e-f3e9953d8649"
/>
## 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
- [x] 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 fixes the conversation list showing raw "**in less than a
minute**" text instead of "**now**" for very recent conversations.
Fixes https://linear.app/chatwoot/issue/CW-6666/issue-with-timestamps
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## 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
Webhook retry failure logs for agent-bot now include the event payload,
making it easier to identify which event failed when debugging transient
upstream errors (429/500).
Previously the log only showed:
`[AgentBots::WebhookJob] attempt 1 failed
RestClient::InternalServerError`
Now it includes the payload:
`[AgentBots::WebhookJob] attempt 1 failed
RestClient::InternalServerError payload={"event":"message_created",...}`
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
# Pull Request Template
## Description
- Skip handoff when the conversation is no longer pending.
- Fetch conversation status with Conversation.uncached to avoid stale
query cache when checking pending state.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
Difficult to reproduce locally, but seen a couple of conversations where
bot hands off an extra time or Captain re-generates usually due to a
retry on FaradayTimeoutError but at perform time, the conversation
status is stale so it appears as if Captain responded in an open
conversation.
## 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
- [x] Any dependent changes have been merged and published in downstream
modules
# Pull Request Template
## Description
For our account, the conversation completion evaluator was proving to be
too conservative. This resulted in queue noise.
This PR adds a line to handle gibberish/single worded messages with no
further context.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
<img width="3012" height="978" alt="CleanShot 2026-03-23 at 11 44 55@2x"
src="https://github.com/user-attachments/assets/328195e8-6ea0-4c3a-9049-ee80196eecad"
/>
<img width="2202" height="866" alt="CleanShot 2026-03-23 at 11 47 15@2x"
src="https://github.com/user-attachments/assets/8d51cff4-b18f-4582-9455-8119ec7eff3a"
/>
## 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
- [x] Any dependent changes have been merged and published in downstream
modules
# Pull Request Template
## Description
This PR adds support to auto-focus the editor when clicking reply to
this message, the editor now automatically receives focus so users can
start typing immediately.
Fixes
https://linear.app/chatwoot/issue/CW-6661/typing-box-not-focused-after-clicking-reply-to-message
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
### Screencast
https://github.com/user-attachments/assets/c5e77055-3f68-4ad8-934e-cfc465166e8a
## 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
## Description
The RemoveOrphanConversationsService filters orphan conversations by a
time window before deleting them. Previously it used created_at, which
could miss old conversations that still had recent activity.
Switching to last_activity_at ensures the cleanup window reflects actual
conversation activity rather than creation time.
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
- By running Rake task
- Run the job from console
## 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
# Pull Request Template
## Description
Captain v1 does not have access to contact attributes. Added a toggle to
let user choose if they want contact information available to Captain.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
Specs and locally
<img width="1924" height="740" alt="CleanShot 2026-03-19 at 18 48 19@2x"
src="https://github.com/user-attachments/assets/353cfeaa-cd58-40eb-89e7-d660a1dc1185"
/>
![Uploading CleanShot 2026-03-19 at 18.53.26@2x.png…]()
## 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
- [x] Any dependent changes have been merged and published in downstream
modules
## Description
Add missing JSON imports and spread exports for `companies`,
`contentTemplates`, `mfa`, `snooze`, `webhooks`, and `yearInReview` so
these translations are properly loaded in the pt_BR locale. Without
these imports, those sections of the UI were falling back to English for
Brazilian Portuguese users.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## 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
Co-authored-by: Sojan Jose <sojan@pepalo.com>
## PR#1: Reporting events rollup — model and write path
Reporting queries currently hit the `reporting_events` table directly.
This works, but the table grows linearly with event volume, and
aggregation queries (counts, averages over date ranges) get
progressively slower as accounts age.
This PR introduces a pre-aggregated `reporting_events_rollups` table
that stores daily per-metric, per-dimension (account/agent/inbox)
totals. The write path is intentionally decoupled from the read path —
rollup rows are written inline from the event listener via upsert, and a
backfill service exists to rebuild historical data from raw events.
Nothing reads from this table yet.
The write path activates when an account has a `reporting_timezone` set
(new account setting). The `reporting_events_rollup` feature flag
controls only the future read path, not writes — so rollup data
accumulates silently once timezone is configured. A `MetricRegistry`
maps raw event names to rollup column semantics in one place, keeping
the write and (future) read paths aligned.
### What changed
- Migration for `reporting_events_rollups` with a unique composite index
for upsert
- `ReportingEventsRollup` model
- `reporting_timezone` account setting with IANA timezone validation
- `MetricRegistry` — single source of truth for event-to-metric mappings
- `RollupService` — real-time upsert from event listener
- `BackfillService` — rebuilds rollups for a given account + date from
raw events
- Rake tasks for interactive backfill and timezone setup
- `reporting_events_rollup` feature flag (disabled by default)
### How to test
1. Set a `reporting_timezone` on an account
(`Account.first.update!(reporting_timezone: 'Asia/Kolkata')`)
2. Resolve a conversation or trigger a first response
3. Check `ReportingEventsRollup.where(account_id: ...)` — rows should
appear
4. Run backfill: `bundle exec rake reporting_events_rollup:backfill` and
verify historical data populates
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
## Description
This PR updates the OpenAPI schema definitions for Public API resources
(`public_conversation`, `public_message`, `public_contact`) so they
match the actual API responses produced by the jbuilder views.
These definitions were introduced in #2417 (2021-06) with a minimal set
of fields. The jbuilder views have since been updated (e.g. `uuid` in
#7255, `agent_last_seen_at` in #4377), but the Swagger definitions were
never updated. As a result, generated API clients get incorrect or
missing types. This change fixes that by aligning the schemas with the
implementation.
**Fixes #13692**
## 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)
- [x] This change requires a documentation update
## How Has This Been Tested?
- Compared each jbuilder view
(`app/views/public/api/v1/models/_conversation.json.jbuilder`,
`_message.json.jbuilder`, `_contact.json.jbuilder`) field-by-field
against the Swagger YAML definitions.
- Cross-referenced Ruby model enums (`Conversation.status`,
`Attachment.file_type`, `Message.message_type`) for enum values.
- Ran the swagger build (via the project’s `rake swagger:build` logic /
`json_refs` resolution) to regenerate `swagger.json` and tag group
files; confirmed the generated schemas contain the correct fields and
types.
- No runtime tests were run; this is a documentation/schema-only change.
## 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 *(N/A: schema definitions)*
- [x] 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 *(N/A: documentation/schema only)*
- [ ] New and existing unit tests pass locally with my changes *(N/A: no
code under test)*
- [x] Any dependent changes have been merged and published in downstream
modules *(N/A: none)*
---
## Change summary (for reference)
### `public_conversation`
- Added missing fields: `uuid`, `status`, `contact_last_seen_at`,
`agent_last_seen_at`
- Fixed `inbox_id` type from `string` to `integer`
- Fixed `messages` items `$ref` from `message` to `public_message`
- Added property details for embedded `contact` object (`id`, `name`,
`email`, `phone_number`)
- Added `status` enum: `open`, `resolved`, `pending`, `snoozed`
### `public_message`
- Fixed `id`, `message_type`, `created_at`, `conversation_id` types
(string → integer where applicable)
- Fixed `content_attributes` type from `string` to `object`
- Added property details for `attachments` items and `sender` object
- Added `file_type` and `sender.type` enums
### `public_contact`
- Added missing `phone_number` field
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
Devise 4.9.x has a race condition in the reconfirmable flow where
concurrent email change requests can desynchronize the confirmation
token from `unconfirmed_email`, letting an attacker confirm an email
they don't own. We use `:confirmable` with `reconfirmable = true`, so
we're directly exposed.
The upstream fix is in Devise 5.0.3, but we can't upgrade —
`devise-two-factor` only supports Devise 5 from v6.4.0, which also
raised its Rails minimum to 7.2+. No released version supports both
Devise 5 and Rails 7.1.
This PR ports the Devise 5.0.3 fix locally by overriding
`postpone_email_change_until_confirmation_and_regenerate_confirmation_token`
on the User model to persist the record before regenerating the token.
This is a stopgap — remove it once the dependency chain allows upgrading
to Devise 5.
### How to test
Sign in as a confirmed user and change your email. The app should send a
confirmation to the new address while keeping the current email
unchanged until confirmed.
The Platform API was returning the bot's `name` value for the
`outgoing_url` field across all agent bot endpoints (index, show,
create, update). A typo in the `_agent_bot.json.jbuilder` partial used
`resource.name` instead of `resource.outgoing_url`.
Closes#13787
## What changed
- `app/views/platform/api/v1/models/_agent_bot.json.jbuilder`: corrected
`resource.name` → `resource.outgoing_url` on the `outgoing_url` field.
## How to reproduce
1. Create an agent bot via `POST /platform/api/v1/agent_bots` with a
distinct `outgoing_url`.
2. Call `GET /platform/api/v1/agent_bots/:id`.
3. Before this fix the `outgoing_url` in the response equals the bot's
`name`; after the fix it equals the value set at creation.
## Tests
Added `outgoing_url` assertions to the existing GET index and GET show
request specs so the regression is covered.
# Pull Request Template
## Description
we were getting 403, 401 errors on `translate_query` on langfuse and
sentry
This happened because, we use the customer's openai key if they have
BYOK
But translation is something they never opt in so we should not use
their quota for it.
This PR addresses the issue.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
locally
## 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
- [x] Any dependent changes have been merged and published in downstream
modules
When `allowed_domains` is configured on a web widget inbox, the server
responds with Content-Security-Policy: frame-ancestors <domains>, which
blocks the widget iframe in mobile app WebViews. This happens because
WebViews load content from file:// or null origins, which cannot match
any domain in the frame-ancestors directive.
This adds a per-inbox toggle — "Enable widget in mobile apps" — that
skips the frame-ancestors header when the request has no valid Origin
(i.e., it comes from a mobile WebView). Web browsers with a real origin
still get domain restrictions enforced as usual.
<img width="2330" height="1490" alt="CleanShot 2026-03-11 at 10 13
01@2x"
src="https://github.com/user-attachments/assets/d9326fac-020d-4ce7-9ced-0c185468c8fc"
/>
Fixes
https://linear.app/chatwoot/issue/CW-6560/widget-is-not-loading-from-iosandroid-widgets
How to test
1. Go to Settings → Inboxes → (Web Widget) → Configuration
2. Set allowed_domains to a specific domain (e.g., *.example.com)
3. Try loading the widget in a mobile app WebView — it should be blocked
4. Enable "Enable widget in mobile apps" checkbox
5. Reload the widget in the WebView — it should now load successfully
6. Verify the widget on a website not in the allowed domains list is
still blocked
---------
Co-authored-by: iamsivin <iamsivin@gmail.com>
This change spreads Chatwoot Hub version checks across the day by
scheduling each installation at a stable minute derived from its
installation identifier, instead of having all instances check at the
same fixed time.
Closes
-
https://linear.app/chatwoot/issue/CW-6107/handle-the-spike-at-12-utc-on-chatwoot-hub
What changed
- Added `Internal::TriggerDailyScheduledItemsJob` to act as the daily
trigger for deferred internal jobs.
- Updated the version check cron entry to run once daily at `00:00 UTC`
and enqueue the actual version check for that installation’s assigned
minute of the day.
- Used a deterministic minute-of-day derived from
`ChatwootHub.installation_identifier` so the check time stays stable
across deploys and restarts.
- Kept the existing cron schedule key while switching it to the new
orchestrator job.
How to test
- Run `bundle exec rspec
spec/jobs/internal/check_new_versions_job_spec.rb
spec/jobs/internal/trigger_daily_scheduled_items_job_spec.rb
spec/configs/schedule_spec.rb`
- In a Rails console, run
`Internal::TriggerDailyScheduledItemsJob.perform_now` and verify
`Internal::CheckNewVersionsJob` is enqueued with a `wait_until` later
the same UTC day.
- In Super Admin settings, use Refresh and verify the version check
still runs immediately.
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
This adds a draft status for Help Center locales so teams can prepare
localized content in the dashboard without exposing those locales in the
public portal switcher until they are ready to publish.
Fixes: https://github.com/chatwoot/chatwoot/issues/10412
Closes: https://github.com/chatwoot/chatwoot/issues/10412
## Why
Teams need a way to work on locale-specific Help Center content ahead of
launch. The public portal should only show ready locales, while the
admin dashboard should continue to expose every allowed locale for
ongoing article and category work.
## What this change does
- Adds `draft_locales` to portal config as a subset of `allowed_locales`
- Hides drafted locales from the public portal language switchers while
keeping direct locale URLs working
- Keeps drafted locales fully visible in the admin dashboard for article
and category management
- Adds locale actions to move an existing locale to draft, publish a
drafted locale, and keep the default locale protected from drafting
- Adds a status dropdown when creating a locale so new locales can be
created as `Published` or `Draft`
- Returns each admin locale with a `draft` flag so the locale UI can
reflect the public visibility state
## Validation
- Seed a portal with multiple locales, draft one locale, and confirm the
public portal switcher hides it while `/hc/:slug/:locale` still loads
directly
- In the admin dashboard, confirm drafted locales still appear in the
locale list and remain selectable for articles and categories
- Create a new locale with `Draft` status and confirm it stays out of
the public switcher until published
- Move an existing locale back and forth between `Published` and `Draft`
and confirm the public switcher updates accordingly
## Demo
https://github.com/user-attachments/assets/ba22dc26-c2e7-463a-b1f5-adf1fda1f9be
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Fixes help center public article search so query responses stay compact
and locale-scoped. Whitespace-only queries are now treated as empty in
both the portal UI and the server-side search path, and search
suggestions stay aligned with the trimmed query.
Fixes: https://github.com/chatwoot/chatwoot/issues/10402
Closes: https://github.com/chatwoot/chatwoot/issues/10402
## Why
The public help center search endpoint reused the full article
serializer for query responses, which returned much more data than the
search suggestions UI needed. That made responses heavier than necessary
and also surfaced nested portal and category data that made the results
look cross-locale.
Whitespace-only searches could also still reach the backend search path,
and in Enterprise that meant embedding search could be invoked for a
blank query.
## What changed
- return a compact search-specific payload for article query responses
- keep the existing full article serializer for normal article listing
responses
- preserve current-locale search behavior for the portal search flow
- trim whitespace-only search terms on the client so they do not open
suggestions or trigger a request
- reuse the normalized query on the backend so whitespace-only requests
are treated as empty searches in both OSS and Enterprise paths
- pass the trimmed search term into suggestions so highlighting matches
the actual query being sent
- add request and frontend regression coverage for compact payloads,
locale scoping, and whitespace-only search behavior
## Validation
1. Open `/hc/:portal/:locale` in the public help center.
2. Enter only spaces in the search box and confirm suggestions do not
open.
3. Search for a real term and confirm suggestions appear.
4. Verify the results are limited to the active locale.
5. Click a suggestion and confirm it opens the correct article page.
6. Inspect the query response and confirm it returns the compact search
payload instead of the full article serializer.
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Email inbox replies now work for Google and Microsoft OAuth inboxes even
when the self-hosted instance does not have global SMTP configured. This
keeps agent replies working for email channels that already have valid
inbox-level delivery settings.
fixes: chatwoot/chatwoot#13118closes: chatwoot/chatwoot#13118
## Why
Self-hosted email inbox replies were blocked by a global SMTP guard in
the `email_reply` path. For OAuth-backed email inboxes, outbound
delivery is configured at the inbox level, so the mailer returned early
and the reply flow failed before sending.
## What this change does
- Allows the `email_reply` path to proceed when the inbox has SMTP
configured
- Allows the `email_reply` path to proceed when the inbox has Google or
Microsoft OAuth delivery configured
- Renames the touched mailer helper predicates to `?` methods for
clarity
## Validation
- Configure a Google email inbox on a self-hosted instance without
global `SMTP_ADDRESS`
- Reply from Chatwoot to an existing email conversation
- Confirm the reply is sent through the inbox OAuth SMTP configuration
- Run `bundle exec rspec
spec/mailers/conversation_reply_mailer_spec.rb:595`
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
https://linear.app/chatwoot/issue/CW-6595/vanta-remediate-high-vulnerabilities-identified-in-packages-are
## Description
- Added "rollup": ">=4.59.0" to pnpm.overrides in package.json
- This bumps rollup from 4.52.5 to 4.59.0 (the transitive dep via vite)
## Type of change
- [ ] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
- Overall Sanity via UI.
## 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: Muhsin Keloth <muhsinkeramam@gmail.com>
## Description
This PR improves the Ukrainian translation for the Chatwoot widget
(`app/javascript/widget/i18n/locale/uk.json`).
Key changes:
- Fixed typo: `Звантажити` → `Завантажити`
- Translated missing English strings
- Improved reply time messages
- Updated day names to match `{day}` usage in `BACK_ON_DAY`
- Improved UX wording in form placeholders
- Fixed typography in `ім’я`
- Improved consistency with other Chatwoot translations
These updates improve readability and correctness of the Ukrainian
widget interface.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Reviewed the updated translations and verified that:
- Ukrainian translations render correctly
- Reply time messages display properly
- `{day}` values work correctly with the `BACK_ON_DAY` message
- Form placeholders appear correctly
- No untranslated English strings remain
## 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
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
This updates the Traditional Chinese (`zh_TW`) locale coverage across
Chatwoot so the app no longer falls back to English for missing backend,
dashboard, widget, and survey strings.
## How to test
1. Start Chatwoot locally and switch the UI locale to Traditional
Chinese (`zh_TW`).
2. Walk through the main product areas: dashboard, settings, inbox
management, help center, automations, reports, widget, and survey flows.
3. Confirm the UI surfaces translated Traditional Chinese copy instead
of English fallbacks.
4. Spot-check newly added locale surfaces such as secure password
messaging and snooze UI copy.
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
Translate all English strings to Korean across 41 frontend locale files
and 2 backend locale files. Add structurally missing keys and translate
existing keys that were left in English.
# Pull Request Template
## Description
Please include a summary of the change and issue(s) fixed. Also, mention
relevant motivation, context, and any dependencies that this change
requires.
Fixes # (issue)
## 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?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
## 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
- [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
- [x] Any dependent changes have been merged and published in downstream
modules
Co-authored-by: Sojan Jose <sojan@pepalo.com>
# Pull Request Template
## Description
Updated few i18n files to:
1. fix typos / grammar / punctuation
2. translate strings that were still in english
3. add missing keys
## Type of change
- [ ] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
i18n change, the format remained the same.
## 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: Sojan Jose <sojan@pepalo.com>
# Pull Request Template
## Description
WhatsApp template messages are treated as outgoing human messages so
conversations are automatically marked Open, preventing Captain from
responding. #13673
This PR fixes the behaviour for template messages, so that captain can
respond to them.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
## 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
- [x] Any dependent changes have been merged and published in downstream
modules
## Description
Fixes the reversed message delivery status indicators for the API
channel. The API inbox was grouped with the web widget inbox in the
`isDelivered` computed property, causing both to treat a `sent` status
as `delivered`. Since the API channel provides real
`sent`/`delivered`/`read` status values from external systems (unlike
the web widget which has no separate delivery confirmation), the API
inbox needs its own handling.
**Before this fix:**
- Status `sent` (0) → incorrectly showed delivered checkmarks
- Status `delivered` (1) → incorrectly showed "Sending" spinner
**After this fix:**
- Status `sent` → correctly shows sent indicator (single checkmark)
- Status `delivered` → correctly shows delivered indicator (double
checkmarks)
- Status `read` → unchanged (already worked correctly)
The web widget inbox behavior is unchanged — it still treats `sent` as
`delivered` since it lacks a separate delivery confirmation mechanism.
Fixes#13576
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Verified by code review that the computed properties now correctly map
API channel message statuses:
- `isSent` returns `true` when `status === 'sent'` for API inbox
- `isDelivered` returns `true` when `status === 'delivered'` for API
inbox
- `isRead` unchanged — already checks `status === 'read'` for API inbox
- Web widget inbox logic is unchanged
## 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] My changes generate no new warnings
*This PR was created with the assistance of Claude Opus 4.6 by
Anthropic. Happy to make any adjustments! Reviewed and submitted by a
human.*
Co-authored-by: Your Name <your-email@example.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
## Description
When an agent resolves or snoozes a conversation, their capacity frees
up — but under Assignment V2, no new conversation was assigned until the
next event triggered the assignment job. This meant agents could sit
idle despite having queued conversations waiting. This change triggers
the AssignmentJob immediately on resolve/snooze so freed capacity is
utilized right away.
## Type of change
- [ ] New feature (non-breaking change which adds functionality)
## How Has This Been Tested?
- Enable auto assignment v2 on an inbox
- Create a conversation (gets auto-assigned to an available agent)
- Resolve the conversation
- Verify that another unassigned conversation in the inbox gets assigned
to the freed-up agent
## 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: tds-1 <tds-1@users.noreply.github.com>
## Description
Adds webhook configuration management for WhatsApp Cloud API channels,
allowing administrators to check webhook status and register webhooks
directly from Chatwoot without accessing Meta Business Manager.
## Type of change
- [ ] New feature (non-breaking change which adds functionality)
## Screenshots
<img width="1130" height="676" alt="Screenshot 2026-03-05 at 7 04 18 PM"
src="https://github.com/user-attachments/assets/f5dcd9dd-8827-42c5-a52b-1024012703c2"
/>
<img width="1101" height="651" alt="Screenshot 2026-03-05 at 7 04 29 PM"
src="https://github.com/user-attachments/assets/e0bd59f9-2a90-4f24-87c0-b79f21e721ee"
/>
## 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: Muhsin Keloth <muhsinkeramam@gmail.com>
## Linear tickets
-
https://linear.app/chatwoot/issue/CW-6607/vanta-remediate-medium-vulnerabilities-identified-in-packages-are
-
https://linear.app/chatwoot/issue/CW-6612/vanta-remediate-medium-vulnerabilities-identified-in-packages-are
## Description
Upgrades markdown-it from 13.0.2 to 14.1.1 to remediate CVE-2026-2327
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
- Sanity testing of golden flows via UI
## 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
# Pull Request Template
## Description
Add account setting and store_accessor for
`captain_force_legacy_auto_resolve`.
Enterprise job now skips LLM evaluation when this flag is true and falls
back to legacy time-based resolution. Add spec to cover the fallback.
## Type of change
We recently rolled out Captain deciding if a conversation is resolved or
not. While it is an improvement for majority of customers, some still
prefer the old way of auto-resolving based on inactivity. This PR adds a
check.
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
legacy_auto_resolve = true
<img width="1282" height="848" alt="CleanShot 2026-03-13 at 19 55 55@2x"
src="https://github.com/user-attachments/assets/dfdcc5d5-6d21-462b-87a6-a5e1b1290a8b"
/>
legacy_auto_resolve = false
<img width="1268" height="864" alt="CleanShot 2026-03-13 at 20 00 50@2x"
src="https://github.com/user-attachments/assets/f4719ec6-922a-4c3b-bc45-7b29eaced565"
/>
## 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
- [x] Any dependent changes have been merged and published in downstream
modules
# Pull Request Template
## Description
Add an api_key override so internal conversation completions prefers
using the system API key and do not consume customer OpenAI credits.
## 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?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
specs and locally
## 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
- [x] Any dependent changes have been merged and published in downstream
modules
SAML sign-in now only links an existing user when that user already
belongs to the account that initiated SSO. New users can still be
created for SAML-enabled accounts, and invited members can continue to
sign in through their IdP, but SAML will no longer auto-attach an
unrelated existing user record during login.
**What changed**
- Added an account-membership check before SAML reuses an existing user
by email.
- Kept first-time SAML user creation unchanged for valid new users.
- Added builder and request specs covering the allowed and rejected
login paths.
# Pull Request Template
## Description
CJK language users (Chinese, Japanese, Korean, etc.) use IME where Enter
confirms character selection. AI input components were intercepting
Enter unconditionally, making them unusable for IME users.
Add `event.isComposing` check to CopilotEditor, CopilotInput, and
AssistantPlayground so Enter during active IME composition is ignored.
## 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?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
Before:
Add a Japenese keyboard, then go to AI follow-ups, type some word,
selecting it with enter submits the follow up. So CJK users cannot use
follow-ups.
https://github.com/user-attachments/assets/53517432-d97b-47fc-a802-81675e31d5c9
After:
Type a word, press enter to choose it, press enter again to unselect it
and enter again to send
https://github.com/user-attachments/assets/6c2a420b-7ee6-4c71-82a6-d9f1d7bbf31a
## 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
- [x] Any dependent changes have been merged and published in downstream
modules
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
# Pull Request Template
## Description
captain decides if conversation should be resolved or open
Fixes
https://linear.app/chatwoot/issue/AI-91/make-captain-resolution-time-configurable
Update: Added 2 entries in reporting events:
`conversation_captain_handoff` and `conversation_captain_resolved`
## Type of change
Please delete options that are not relevant.
- [x] New feature (non-breaking change which adds functionality)
- [x] This change requires a documentation update
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
LLM call decides that conversation is resolved, drops a private note
<img width="1228" height="438" alt="image"
src="https://github.com/user-attachments/assets/fb2cf1e9-4b2b-458b-a1e2-45c53d6a0158"
/>
LLM call decides conversation is still open as query was not resolved
<img width="1215" height="573" alt="image"
src="https://github.com/user-attachments/assets/2d1d5322-f567-487e-954e-11ab0798d11c"
/>
## 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
- [x] Any dependent changes have been merged and published in downstream
modules
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
When the SDK sends identify calls with identical payloads (common on
every page load), `save!` fires even though no attributes changed. While
Rails skips the actual UPDATE SQL, it still opens a transaction, runs
all callbacks (including validation queries like `Contact Exists?`), and
triggers `after_commit` hooks — all for a no-op.
This adds a `changed?` guard before `save!` to skip it entirely when no
attributes have actually changed.
**How to test**
- Trigger an identify call via the SDK with a contact's existing
attributes (same name, email, custom_attributes, etc.)
- The contact should not fire a save (no transaction, no callbacks)
- Trigger an identify call with a changed attribute — save should work
normally
**What changed**
- `ContactIdentifyAction#update_contact`: guard `save!` with `changed?`
check
- Added specs to verify `save!` is skipped for unchanged params and
avatar job still enqueues independently
Removes touch: true from the belongs_to :conversation association on
Message and consolidates the conversation timestamp update into the
existing set_conversation_activity callback.
Previously, every message save triggered two separate UPDATE queries on
the conversation — one from Rails' touch (updating updated_at) and
another from set_conversation_activity (updating last_activity_at). This
combines both into a single update_columns call, reducing write load on
the conversations table on every message creation.
### What changed
- Removed touch: true from belongs_to :conversation in Message
- Added updated_at: created_at to the existing update_columns call in
set_conversation_activity
## Description
Remediates high severity ReDoS vulnerability in minimatch
(CVE-2026-27903) flagged by Vanta/Dependabot.
minimatch is a transitive dev-only dependency (via eslint and
tailwindcss build tooling) — not shipped to production. Added pnpm
overrides to force patched versions:
- minimatch@<4 → 3.1.5
- minimatch@>=9.0.0 <9.0.7 → 9.0.9
Closes:
https://linear.app/chatwoot/issue/CW-6595/vanta-remediate-high-vulnerabilities-identified-in-packages-are
## Type of change
- [ ] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
- No production impact — minimatch is only used in dev tooling, not at
runtime
- pnpm install completes successfully
## 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
# Pull Request Template
## Description
Unconfirmed agents (pending email verification) were incorrectly
appearing in the "assign agent" dropdown for macros and automations.
This fix filters out unconfirmed agents from these dropdowns and adds
backend validation to prevent assignment of unconfirmed agents.
Fixes#13223
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
**Backend tests:**
```bash
docker compose run --rm rails bundle exec rspec spec/services/action_service_spec.rb
```
- Added tests for confirmed agent assignment (should succeed)
- Added tests for unconfirmed agent assignment (should be skipped)
**Frontend tests:**
```bash
docker compose run --rm rails pnpm test app/javascript/dashboard/composables/spec/useMacros.spec.js
```
- Updated mocks to use `getVerifiedAgents` getter
**Manual testing:**
1. Create an unconfirmed agent via platform
2. Navigate to Settings → Macros → New Macro → Add "Assign Agent" action
3. Verify unconfirmed agent does NOT appear in dropdown
4. Navigate to Settings → Automations → New Automation → Add "Assign
Agent" action
5. Verify unconfirmed agent does NOT appear in dropdown
## 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
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
# Pull Request Template
## Description
Playground now uses v2. It was only wired to use v1. Traces get `source:
playground` on langfuse when playground has been used.
## Type of change
- [x] New feature (non-breaking change which adds functionality)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
locally and specs
<img width="1806" height="1276" alt="image"
src="https://github.com/user-attachments/assets/41ef4eb3-52b1-4b8e-9a4f-e8510c90cb39"
/>
## 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
- [x] Any dependent changes have been merged and published in downstream
modules
# Pull Request Template
## Description
Ensure agent function names stay within OpenAI's 64-char limit
(ai-agents prepends "handoff_to_").
Add HANDOFF_TITLE_SLUG_MAX_LENGTH and
handoff_key generation: persisted records use `scenario_{id}_agent`; new
records use a truncated title slug.
Assistant scenario keys and agent_name now reference the generated
handoff key.
fixes :
`Invalid 'messages[9].tool_calls[0].function.name': string too long.
Expected a string with maximum length 64, but got a string with length
95 instead.`
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
Tested locally
<img width="1806" height="1044" alt="image"
src="https://github.com/user-attachments/assets/40cd7a3d-3d97-43a8-bd56-d3f5d63abbda"
/>
## 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
- [x] Any dependent changes have been merged and published in downstream
modules
---------
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
## Description
Makes the assignment_v2 feature flag available to all installations by
removing the chatwoot_internal restriction. Previously this feature was
hidden from self-hosted installations; this change surfaces it in the
feature flags UI so any Chatwoot instance can enable it.
## 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 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
This PR generally upgrades the available packages clearing existing
transient package vulnerabilities
### minimatch
This is mostly a dev-dependency, it was in the main dependency for
`@formkit/vue`, this PR clears it.
It still remains a dependency for tailwind and tailwind typograpghy, but
it's tolerable
_Fixes: CW-6594 CW-6592 CW-6598_
### Axios
Upgraded the dependency directly. Fixed it
_Fixes: CW-6591_
### dompurify
Upgraded the dependency, it's in the safe range now
_Fixes: CW-6611 CW-6610_
### ajv
Dev dependency, can be safely ignored
_Fixes: CW-6606 CW-6604_
### @tootallnate/once
Dev dependency, comes from Histoire, can be safely ignored
_Fixes: CW-6603_
Documents the missing account-level label CRUD endpoints in Chatwoot's
Swagger output so label management is discoverable alongside the
existing contact and conversation label APIs.
Fixes: none
Closes: none
Why
The account-level label API already exists in Chatwoot, but it was
missing from the published Swagger spec. That made label management
harder to discover even though contact and conversation label assignment
endpoints were already documented.
What this change does
- adds a `Labels` tag to the application Swagger docs
- adds the label resource and create/update payload schemas
- documents `GET/POST /api/v1/accounts/{account_id}/labels`
- documents `GET/PATCH/DELETE /api/v1/accounts/{account_id}/labels/{id}`
- regenerates the compiled Swagger JSON artifacts
Validation
- rebuilt the Swagger JSON from the source YAML
- verified the new label endpoints appear in `swagger/swagger.json`
- verified the new label endpoints appear in
`swagger/tag_groups/application_swagger.json`
- started the local Rails server and confirmed `/swagger` and
`/swagger/swagger.json` return `200 OK`
Adds Skooma-based OpenAPI validation so SDK-facing request specs can
assert that documented request and response contracts match real Rails
behavior. This also upgrades the spec to OpenAPI 3.1 and fixes contract
drift uncovered while validating core application and platform
resources.
Closes
None
Why
We want CI to catch OpenAPI drift before it reaches SDK consumers. While
wiring validation in, this PR surfaced several mismatches between the
documented contract and what the Rails endpoints actually accept or
return.
What this change does
- Adds Skooma-backed OpenAPI validation to the request spec flow and a
dedicated OpenAPI validation spec.
- Migrates nullable schema definitions to OpenAPI 3.1-compatible unions.
- Updates core SDK-facing schemas and payloads across accounts,
contacts, conversations, inboxes, messages, teams, reporting events, and
platform account resources.
- Documents concrete runtime cases that were previously missing or
inaccurate, including nested `profile` update payloads, multipart avatar
uploads, required profile update bodies, nullable inbox feature flags,
and message sender types that include both `Captain::Assistant` and
senderless activity-style messages.
- Regenerates the committed Swagger JSON and tag-group artifacts used by
CI sync checks.
Validation
- `bundle exec rake swagger:build`
- `bundle exec rspec spec/swagger/openapi_spec.rb`
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
## Description
This PR optimizes message queries by explicitly filtering with
`account_id` so the database can use the existing indexes more
efficiently.
Changes:
- Add `account_id` to message query filters to improve index
utilization.
- Update `last_incoming_message` query to include `account_id`.
- Avoid unnecessary preloading of `contact_inboxes` where it is not
required.
- Update specs to ensure `account_id` is set correctly in
message-related tests.
These changes reduce query cost and improve performance for message
lookups, especially on large accounts.
---------
Co-authored-by: Pranav <pranav@chatwoot.com>
This makes account signup enforcement consistent when signup is disabled
at the installation level. Email signup and Google signup now stay
blocked regardless of whether the config value is stored as a string or
a boolean.
This effectively covers the config-loader path, where `YAML.safe_load`
reads `value: false` from `installation_config.yml` as a native boolean
and persists it that way.
- Normalized the account signup check so disabled signup is handled
consistently across config value types.
- Reused the same check across API signup and Google signup entry
points.
- Added regression coverage for the disabled-signup cases in the
existing controller specs.
---------
Co-authored-by: Vishnu Narayanan <iamwishnu@gmail.com>
- Extracted 6 custom attribute methods (`custom_attribute_query`,
`attribute_model`, `attribute_data_type`, `build_custom_attr_query`,
`custom_attribute`, `not_in_custom_attr_query`) into a new
`Filters::CustomAttributeFilterHelper` module.
- Added an inline `rubocop:disable` for the intentional
`Lint/ShadowedException` in `coerce_lt_gt_value` — `Date::Error` is a
subclass of `ArgumentError`, but both are listed explicitly for clarity.
## Why `app/services/filters/`
The existing `Filters::FilterHelper` lives in `app/helpers/filters/`,
but that location triggers `Rails/HelperInstanceVariable` for any module
that uses instance variables. The extracted methods share state with
`FilterService` via instance variables (`@attribute_key`, `@account`,
`@custom_attribute`, etc.), so placing them in `app/helpers/` would
require a cop disable.
`app/services/filters/` is a better fit because:
- The module is a service mixin, not a view helper — it's only included
by `FilterService` and its subclasses (`Conversations::FilterService`,
`Contacts::FilterService`, `AutomationRules::ConditionsFilterService`).
- It sits alongside the services that use it.
- No cop disables needed.
---------
Co-authored-by: Vishnu Narayanan <iamwishnu@gmail.com>
## Summary
- The conversation transcript endpoint rate limit is hardcoded at 30
requests/hour per account with no way to override it
- Self-hosted users with active accounts hit this limit and get 429
errors across all channels
- Add `RATE_LIMIT_CONVERSATION_TRANSCRIPT` env var (default: `1000`) to
make it configurable, consistent with other throttles like
`RATE_LIMIT_CONTACT_SEARCH` and `RATE_LIMIT_REPORTS_API_ACCOUNT_LEVEL`
On self-hosted instances without email configured, users created from
Super Admin can get stuck in an unconfirmed state. This PR implements
the default at the Super Admin frontend form layer, not in backend
creation logic.
What changed:
- Added a custom `ConfirmedAtField` for Super Admin user forms.
- Prefills `confirmed_at` with current time on the **New User** form
(`GET /super_admin/users/new`).
- Kept backend create behavior unchanged
(`resource_class.new(resource_params)`), so API/manual payloads still
behave normally.
Behavior:
- In Super Admin UI, `confirmed_at` is prefilled by default.
- If someone wants an unconfirmed user, they can clear the
`confirmed_at` field before saving.
- If `confirmed_at` is omitted from payload entirely, the created user
remains unconfirmed.
Scope note: external signup flows are intentionally unchanged in this PR
(`/api/v1/accounts`, `/api/v2/accounts`, and social/omniauth signup
behavior are not modified).
## Demo
https://github.com/user-attachments/assets/436abbb0-d4cf-49a6-a1b8-4b6aa85aa09f
Description:
## Summary
- `redis-client` 0.22.2 uses `.call()` during Sentinel master
resolution, but `redis-rb` 5.x undefines `.call()` (only `.call_v()`
exists), causing Sentinel connections to fail.
- Bumps `redis-client` from 0.22.2 to 0.26.4 which includes the upstream
fix (redis-rb/redis-client#283).
- Also bumps transitive dependency `connection_pool` from 2.5.3 to
2.5.5.
Fixes#11665https://github.com/chatwoot/chatwoot/issues/8368
## Test
- `bundle exec rspec spec/lib/redis/config_spec.rb` passes
- Full CI suite passes
## Description
Reduces the frequency of update_presence WebSocket calls from the live
chat widget and fixes agents appearing offline when the dashboard is in
a background tab.
## Fixes # (issue)
https://github.com/chatwoot/chatwoot/issues/13720
## Type of change
- [ ] Bug fix (non-breaking change which fixes an issue)
## 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
Agents using API channel inboxes (e.g., WhatsApp Automate) reported
seeing the same conversation appear twice in their conversation list —
one showing the last message preview and the other showing "No
Messages". Backend investigation confirmed no duplicate conversations
exist in the database, making this purely a frontend issue.
The root cause is a race condition in WebSocket event delivery. When a
conversation is created via the API with auto-assignment, the backend
enqueues multiple ActionCable broadcast jobs (`conversation.created`,
`assignee.changed`, `team.changed`) within milliseconds of each other.
In production with multi-threaded Sidekiq workers, these events can
arrive at the frontend out of order. If `assignee.changed` arrives
before `conversation.created`, the `UPDATE_CONVERSATION` mutation pushes
the conversation into the store (since it doesn't exist yet), and then
`ADD_CONVERSATION` blindly pushes it again — resulting in a duplicate
entry.
The fix adds a uniqueness check in the `ADD_CONVERSATION` mutation to
skip the push if a conversation with the same ID already exists in the
store, matching the dedup pattern already used by
`SET_ALL_CONVERSATION`.
## Summary
This Enterprise-only feature automatically fetches a favicon for
companies created with a domain, and adds a batch task to backfill
missing avatars for existing companies. The flow only targets companies
that do not already have an attached avatar, so existing avatars are
left untouched.
## Demo
https://github.com/user-attachments/assets/d050334e-769f-4e46-b6e7-f7423727a192
## What changed
- Added `Avatar::AvatarFromFaviconJob` to build a Google favicon URL
from the company domain and fetch it through `Avatar::AvatarFromUrlJob`
- Triggered favicon fetching from `Company` with `after_create_commit`
- Added `Companies::FetchAvatarsJob` to batch existing companies that
are missing avatars
- Added `companies:fetch_missing_avatars` under `enterprise/lib/tasks`
- Kept the company-specific implementation inside the Enterprise
boundary
- Stubbed the new favicon request in unrelated specs that now hit this
callback indirectly
- Updated a couple of CI-sensitive specs that were failing due to
callback side effects / reload-safe exception assertions
## How to verify
1. Create a company in Enterprise with a valid domain and no avatar.
2. Confirm that a favicon-based avatar gets attached shortly after
creation.
3. Create another company with a domain and an avatar already attached.
4. Confirm that the existing avatar is not replaced.
5. Run `companies:fetch_missing_avatars`.
6. Confirm that existing companies without avatars get one, while
companies that already have avatars remain unchanged.
## Notes
- This change does not refresh or overwrite existing company avatars
- Favicon fetching only runs for companies with a present domain
- The branch includes the latest `develop`
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Sojan Jose <sojan@pepalo.com>
Agent bot conversations now feel more natural because AgentBot tokens
can toggle typing status, so end users see a live typing indicator in
the widget while the bot is preparing a reply. This keeps the
interaction responsive and human-like without weakening token
authorization boundaries.
## Closes
- https://github.com/chatwoot/chatwoot/issues/8928
- https://linear.app/chatwoot/issue/CW-5205
## How to test
1. Open the widget and start a conversation as a customer.
2. Connect an AgentBot to the same inbox.
3. Trigger `toggle_typing_status` with the AgentBot token
(`typing_status: on`).
4. Confirm the customer sees the typing indicator in the widget.
5. Trigger `toggle_typing_status` with `typing_status: off` and confirm
the indicator disappears.
## What changed
- Added `toggle_typing_status` to bot-accessible conversation endpoints.
- Restricted bot-accessible endpoint usage to `AgentBot` token owners
only (non-user tokens like `PlatformApp` remain unauthorized).
- Updated typing status flow to preserve AgentBot identity in
dispatch/broadcast paths.
- Added request coverage for AgentBot success and PlatformApp
unauthorized behavior.
- Added Swagger documentation for `POST
/api/v1/accounts/{account_id}/conversations/{conversation_id}/toggle_typing_status`
and regenerated swagger artifacts.
## Summary
Improve local dev restart reliability by enhancing `make force_run` to
run cleanup before starting Overmind.
## How To Reproduce
During local development, if `make run` is interrupted (for example with
Ctrl-C), stale state can remain (`.overmind.sock`, PID files, and
processes on ports `3000`/`3036`), which can block or complicate the
next restart.
## Changes
Updated `force_run` in `Makefile` to:
- print cleanup start/end messages
- kill processes on ports `3036` and `3000` (best-effort)
- remove `.overmind.sock`
- remove `tmp/pids/*.pid`
- then start `Procfile.dev` via Overmind
No other files are changed in this PR.
## Testing
- Verified branch diff against `develop` only touches `Makefile`.
- Ran `make -n force_run` to validate the command sequence and startup
flow.
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
# Pull Request Template
## Description
This PR fixes the console warning in development: `[Vue warn]: Missing
required prop: "name"` on the account settings page.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
**Screenshot**
<img width="599" height="1036" alt="image"
src="https://github.com/user-attachments/assets/b0b45854-4cfb-4fe7-ab14-c42a65c523df"
/>
## 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
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.
This PR updates Facebook Messenger outbound tagging in Chatwoot to
support Human Agent messaging when enabled.
Previously, Facebook outbound text and attachment messages were always
sent with:
```
messaging_type: MESSAGE_TAG
tag: ACCOUNT_UPDATE
```
With this change, the tag is selected dynamically:
```
HUMAN_AGENT when ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT is enabled
ACCOUNT_UPDATE as fallback when the flag is disabled
```
This fixes the agent-bot webhook delivery path so transient upstream
failures follow the expected delivery lifecycle. Existing fallback
behavior is preserved, and fallback actions are applied only after
delivery attempts are exhausted.
To reproduce, configure an agent-bot webhook endpoint to return 429/500
for message events. Before this fix, failure handling could be applied
too early; after this fix, delivery attempts complete first and then
existing fallback handling runs.
Tested with:
- bundle exec rspec spec/jobs/agent_bots/webhook_job_spec.rb
spec/lib/webhooks/trigger_spec.rb
- bundle exec rubocop spec/jobs/agent_bots/webhook_job_spec.rb
spec/lib/webhooks/trigger_spec.rb
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Add a temporary `captain_disable_auto_resolve` boolean setting on
accounts to prevent Captain from resolving conversations. Guards both
the scheduled resolution job and the assistant's resolve tool.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Describe the bug
In v4.8.0, when an audio message is received, the system enqueues
Messages::AudioTranscriptionJob even if OpenAI and Captain are disabled.
This causes a Faraday::UnauthorizedError (401) which crashes the Sidekiq
job and breaks the pipeline for that message.
To Reproduce
Disable OpenAI/Captain integrations.
Send an audio message to an inbox.
Check Sidekiq logs and observe the 401 crash in
AudioTranscriptionService.
What this PR does
Adds a rescue Faraday::UnauthorizedError block inside
AudioTranscriptionService#perform. Instead of crashing the worker, it
logs a warning and gracefully exits, allowing the job to complete
successfully.
Note: This fixes the backend crash. However, there is still a frontend
reactivity issue where the audio player UI requires an F5 to load the
media, which has been reported in Issue #11013.
---------
Co-authored-by: Eloi Junior Seganfredo <eloi@seganfredo.local>
Co-authored-by: Aakash Bakhle <48802744+aakashb95@users.noreply.github.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
When agents send replies from the native Facebook Messenger app (not
Chatwoot), echo events were created without external_echo metadata and
could be misrepresented in the UI. This change updates Messenger echo
message creation to:
- set content_attributes.external_echo = true for outgoing_echo messages
- set echo message status to delivered
- keep sender as nil for echo messages (existing behavior)
<img width="2614" height="1264" alt="CleanShot 2026-02-26 at 16 32
04@2x"
src="https://github.com/user-attachments/assets/ba61c941-465d-4893-814e-855e6b6c79e8"
/>
## Notion document
https://www.notion.so/chatwoot/Email-IMAP-Issue-30aa5f274c928062aa6bddc2e5877a63?showMoveTo=true&saveParent=true
## Description
PLAIN IMAP channels (non-OAuth) were silently retrying failed
authentication every minute, forever. When credentials are
wrong/expired, Net::IMAP::NoResponseError was caught and logged but
channel.authorization_error! was never called — so the Redis error
counter never incremented, reauthorization_required? was never set, and
admins were never notified. OAuth channels already had this handled
correctly via the Reauthorizable concern.
Additionally, Net::IMAP::ResponseParseError (raised by non-RFC-compliant
IMAP servers) was falling through to the StandardError catch-all,
flooding
Estimated impact before fix: ~70–75 broken IMAP inboxes generating
~700k–750k wasted Sidekiq jobs/week.
## Type of change
- [ ] Bug fix (non-breaking change which fixes an issue)
## 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
## Docs
https://www.notion.so/chatwoot/Redeeming-a-depreciated-feature-flag-313a5f274c9280f381cdd811eab42019?source=copy_link
## Description
Marks 8 unused feature flags as deprecated: true in features.yml,
freeing their bit slots for future reuse.
Removes dead code references from JS constants, help URLs, and
enterprise billing config.
## Type of change
- [ ] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
- Simulated the "claim a slot" workflow
## 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
- Add a new conversation sort option "Priority: Highest first, Created:
Oldest first" that sorts by priority descending (urgent > high > medium
> low > none) with created_at ascending as the tiebreaker
- Replace `POST /contacts/filter` with `GET /contacts/search` for
contact lookup in compose new conversation
- Remove client-side input-type detection logic (`generateContactQuery`,
key filtering by email/phone/name) — the search API handles matching
across name, email, phone_number, and identifier server-side via a
single `ILIKE` query
- Filter the contacts with emails in cc and bcc fields.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
## Summary
This PR enables and surfaces **conversation workflow** for social-style
channels that should support either:
- `Create new conversations` after resolve, or
- `Reopen same conversation`
## What is included
- Adds the conversation workflow setting UI as card-based options in
Inbox Settings.
- Expands channel availability in settings to include channels like:
- Telegram
- TikTok
- Instagram
- Line
- WhatsApp
- Facebook
- Updates conversation selection behavior for Line incoming messages to
respect the workflow (reopen vs create-new-after-resolved).
- Updates TikTok conversation selection behavior to respect the workflow
(reopen vs create-new-after-resolved).
- Keeps email behavior unchanged (always starts a new thread).
Fixes: https://github.com/chatwoot/chatwoot/issues/8426
## Screenshot
<img width="1400" height="900" alt="pr11079-workflow-sender-clear-tight"
src="https://github.com/user-attachments/assets/9456821f-8d83-4924-8dcf-7503c811a7b1"
/>
## How To Reproduce
1. Open `Settings -> Inboxes ->
<Telegram/TikTok/Instagram/Line/Facebook/WhatsApp inbox> -> Settings`.
2. Verify **Conversation workflow** is visible with the two card
options.
3. Toggle between both options and save.
4. For Line and TikTok, verify resolved-conversation behavior follows
the selected workflow.
## Testing
- `RAILS_ENV=test bundle exec rspec
spec/builders/messages/instagram/message_builder_spec.rb:213
spec/builders/messages/instagram/message_builder_spec.rb:255
spec/builders/messages/instagram/messenger/message_builder_spec.rb:228
spec/builders/messages/instagram/messenger/message_builder_spec.rb:293
spec/services/tiktok/message_service_spec.rb`
- Result: `16 examples, 0 failures`
## Follow-up
- Migrate Website Live Chat workflow settings into this same
conversation-workflow settings model.
- Add Voice channel support for this workflow setting.
---------
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>
# Pull Request Template
## Description
This PR replaces `vue-virtual-scroller` with
[`virtua`](https://github.com/inokawa/virtua/#benchmark) for the
conversation list virtualization.
### Changes
- Replace `vue-virtual-scroller`
(`DynamicScroller`/`DynamicScrollerItem`) with `virtua`'s `Virtualizer`
component
- Remove `IntersectionObserver`-based infinite scroll in favor of
`Virtualizer`'s `@scroll` event with offset-based bottom detection
- Remove `useEventListener` scroll binding and
`intersectionObserverOptions` computed
- Simplify item rendering — no more `DynamicScrollerItem` wrapper or
`size-dependencies` tracking; `virtua` measures items automatically
## 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
- [ ] 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
Extract and pass image attachments from the latest user message to the
runner,
excluding the last user message from the context for processing.
Fixes#13588
# Pull Request Template
## Description
Adds image support to captain v2
## 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?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
specs and local testing
<img width="754" height="1008" alt="image"
src="https://github.com/user-attachments/assets/914cbc2c-9d30-42d0-87d4-9e5430845c87"
/>
langfuse also shows media correctly with the instrumentation code:
<img width="1800" height="1260" alt="image"
src="https://github.com/user-attachments/assets/ce0f5fa6-b1a5-42ec-a213-9a82b1751037"
/>
## 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
- [x] Any dependent changes have been merged and published in downstream
modules
---------
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
CSAT templates for WhatsApp are submitted as Utility, but Meta may
reclassify them as Marketing based on content, which can significantly
increase messaging costs.
This PR introduces a Captain-powered CSAT template analyzer for
WhatsApp/Twilio WhatsApp that predicts utility fit, explains likely
risks, and suggests safer rewrites before submission. The flow is manual
(button-triggered), Captain-gated, and applies rewrites only on explicit
user action. It also updates UX copy to clearly set expectations: the
system submits as Utility, Meta makes the final categorization decision.
Fixes
https://linear.app/chatwoot/issue/CW-6424/ai-powered-whatsapp-template-classifier-for-csat-submissionshttps://github.com/user-attachments/assets/8fd1d6db-2f91-447c-9771-3de271b16fd9
## Summary
This change fixes a mismatch in contact details where Telegram data
could be shown in the contact profile/social icon area but was not
available in the editable contact form.
### What changed
- Added Telegram to the social links section of the next-gen contact
form so agents can view and edit it alongside Facebook, Instagram,
TikTok, Twitter, GitHub, and LinkedIn.
- Added Telegram support to the legacy conversation contact edit form
for parity between both contact editing experiences.
- Mapped social_telegram_user_name into the editable socialProfiles
payload when preparing contact form state, so Telegram usernames sourced
from channel attributes are visible in the form.
- Updated the conversation contact social profile merge logic so
Telegram display prefers an explicitly saved social profile value and
falls back to social_telegram_user_name when needed.
- Added the missing English i18n placeholder: Add Telegram.
### Why
Without this, users could see Telegram info in some contact views but
could not reliably edit it in contact details, creating inconsistent
behavior between display and edit states.
---------
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
When agents or integrations create private notes on a conversation,
every note was sending a notification to the assigned agent and all
conversation participants. The fix ensures that private notes no longer
trigger new message notifications. If someone explicitly mentions a
teammate in a private note, that person will still get notified as
expected.
## Description
AutoAssignment::RateLimiter#within_limit? returned true early for
inboxes without an AssignmentPolicy, bypassing fair distribution
entirely and allowing unlimited conversation assignment.
## Type of change
- [ ] Bug fix (non-breaking change which fixes an issue)
## Script
- Script to enable users with V2 assignment:
https://www.notion.so/chatwoot/Script-to-migrate-account-to-assignment-V2-30ca5f274c9280f5b8ecfd15e28eeb9c?source=copy_link
## 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: Shivam Mishra <scm.mymail@gmail.com>
# Pull Request Template
## Summary
- Adds `perform_meta_only` method to `ConversationFinder` that runs
setup and counts without loading the paginated conversation list
- Updates `/api/v1/conversations/meta` to use `perform_meta_only`
instead of `perform`
## Problem
The `/meta` endpoint calls `ConversationFinder#perform` which:
1. Runs all filters and setup (`set_up`)
2. Computes 3 COUNT queries (`set_count_for_all_conversations`)
3. Filters by assignee type
4. **Builds the full paginated conversation list** with
`.includes(:taggings, :inbox, {assignee: {avatar_attachment: [:blob]}},
{contact: {avatar_attachment: [:blob]}}, :team, :contact_inbox)` +
sorting + pagination
The controller then **discards the conversations** and only uses the
counts:
```ruby
def meta
result = conversation_finder.perform
@conversations_count = result[:count] # conversations thrown away
end
```
## Type of change
- [x] Performance fix
## How Has This Been Tested?
- [ ] Verify /meta returns correct mine/unassigned/assigned/all counts
- [ ] Verify counts update when switching inbox, team, or status filters
- [ ] Verify conversation list still loads correctly (uses perform, not
affected)
- [ ] Monitor response time reduction for /meta in NewRelic after deploy
## 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>
# Pull Request Template
## Description
This PR includes,
1. Adjusting the inbox settings page layout width from 3xl to 4xl for
the collaborators, configuration, and bot configuration sections.
2. Adding a dynamic max-width for inbox settings banners based on the
selected tab.
3. Making the sender name preview layout responsive.
4. Reordering automation rule row buttons so Clone appears before
Delete.
5. Update the Gmail icon ratio.
6. Fix height issues with team/inbox pages
7. The delete button changes to red on hover
8. Add border to conversation header when no dashboard apps present
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## 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
Adds a new built-in tool that allows Captain scenarios to resolve
conversations programmatically. This enables automated workflows like
the misdirected contact deflector to close conversations after handling
them, while still allowing human review via label filtering.
## Type of change
Please delete options that are not relevant.
- [x] New feature (non-breaking change which adds functionality)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
tested by mentioning it to be used in captain v2 scenario
<img width="1180" height="828" alt="image"
src="https://github.com/user-attachments/assets/e70baf96-0c70-407e-af2c-328500ac5434"
/>
## 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
- [x] Any dependent changes have been merged and published in downstream
modules
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Tanmay Deep Sharma <32020192+tds-1@users.noreply.github.com>
# Pull Request Template
## Description
Adds channel type to Captain assistant traces in Langfuse
## Type of change
- [x] New feature (non-breaking change which adds functionality)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
<img width="906" height="672" alt="image"
src="https://github.com/user-attachments/assets/224cee95-56aa-4672-8f74-0c0052251db9"
/>
<img width="908" height="611" alt="image"
src="https://github.com/user-attachments/assets/ddd8ef0d-47c1-450c-a09f-27e82a34d04d"
/>
## 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
- [x] Any dependent changes have been merged and published in downstream
modules
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Bumps [nokogiri](https://github.com/sparklemotion/nokogiri) from 1.18.9
to 1.19.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/sparklemotion/nokogiri/releases">nokogiri's
releases</a>.</em></p>
<blockquote>
<h2>v1.19.1 / 2026-02-16</h2>
<h3>Security</h3>
<ul>
<li>[CRuby] Address unchecked return value from
<code>xmlC14NExecute</code> which was a contributing cause to ruby-saml
GHSA-x4h9-gwv3-r4m4. See <a
href="https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-wx95-c6cv-8532">GHSA-wx95-c6cv-8532</a>
for more information.</li>
</ul>
<!-- raw HTML omitted -->
<pre><code>cfdb0eafd9a554a88f12ebcc688d2b9005f9fce42b00b970e3dc199587b27f32
nokogiri-1.19.1-aarch64-linux-gnu.gem
1e2150ab43c3b373aba76cd1190af7b9e92103564063e48c474f7600923620b5
nokogiri-1.19.1-aarch64-linux-musl.gem
0a39ed59abe3bf279fab9dd4c6db6fe8af01af0608f6e1f08b8ffa4e5d407fa3
nokogiri-1.19.1-arm-linux-gnu.gem
3a18e559ee499b064aac6562d98daab3d39ba6cbb4074a1542781b2f556db47d
nokogiri-1.19.1-arm-linux-musl.gem
dfe2d337e6700eac47290407c289d56bcf85805d128c1b5a6434ddb79731cb9e
nokogiri-1.19.1-arm64-darwin.gem
1e0bda88b1c6409f0edb9e0c25f1bf9ff4fa94c3958f492a10fcf50dda594365
nokogiri-1.19.1-java.gem
110d92ae57694ae7866670d298a5d04cd150fae5a6a7849957d66f171e6aec9b
nokogiri-1.19.1-x64-mingw-ucrt.gem
7093896778cc03efb74b85f915a775862730e887f2e58d6921e3fa3d981e68bf
nokogiri-1.19.1-x86_64-darwin.gem
1a4902842a186b4f901078e692d12257678e6133858d0566152fe29cdb98456a
nokogiri-1.19.1-x86_64-linux-gnu.gem
4267f38ad4fc7e52a2e7ee28ed494e8f9d8eb4f4b3320901d55981c7b995fc23
nokogiri-1.19.1-x86_64-linux-musl.gem
598b327f36df0b172abd57b68b18979a6e14219353bca87180c31a51a00d5ad3
nokogiri-1.19.1.gem
</code></pre>
<!-- raw HTML omitted -->
<h2>v1.19.0 / 2025-12-28</h2>
<h4>Ruby</h4>
<p>This release is focused on changes to Ruby version support, and is
otherwise functionally identical to v1.18.10.</p>
<ul>
<li>Introduce native gem support for Ruby 4.0. <a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/3590">#3590</a></li>
<li>End support for Ruby 3.1, for which <a
href="https://www.ruby-lang.org/en/downloads/branches/">upstream support
ended 2025-03-26</a>.</li>
<li>End support for JRuby 9.4 (which targets Ruby 3.1
compatibility).</li>
</ul>
<!-- raw HTML omitted -->
<pre><code>11a97ecc3c0e7e5edcf395720b10860ef493b768f6aa80c539573530bc933767
nokogiri-1.19.0-aarch64-linux-gnu.gem
eb70507f5e01bc23dad9b8dbec2b36ad0e61d227b42d292835020ff754fb7ba9
nokogiri-1.19.0-aarch64-linux-musl.gem
572a259026b2c8b7c161fdb6469fa2d0edd2b61cd599db4bbda93289abefbfe5
nokogiri-1.19.0-arm-linux-gnu.gem
23ed90922f1a38aed555d3de4d058e90850c731c5b756d191b3dc8055948e73c
nokogiri-1.19.0-arm-linux-musl.gem
0811dfd936d5f6dd3f6d32ef790568bf29b2b7bead9ba68866847b33c9cf5810
nokogiri-1.19.0-arm64-darwin.gem
5f3a70e252be641d8a4099f7fb4cc25c81c632cb594eec9b4b8f2ca8be4374f3
nokogiri-1.19.0-java.gem
05d7ed2d95731edc9bef2811522dc396df3e476ef0d9c76793a9fca81cab056b
nokogiri-1.19.0-x64-mingw-ucrt.gem
1dad56220b603a8edb9750cd95798bffa2b8dd9dd9aa47f664009ee5b43e3067
nokogiri-1.19.0-x86_64-darwin.gem
f482b95c713d60031d48c44ce14562f8d2ce31e3a9e8dd0ccb131e9e5a68b58c
nokogiri-1.19.0-x86_64-linux-gnu.gem
1c4ca6b381622420073ce6043443af1d321e8ed93cc18b08e2666e5bd02ffae4
nokogiri-1.19.0-x86_64-linux-musl.gem
e304d21865f62518e04f2bf59f93bd3a97ca7b07e7f03952946d8e1c05f45695
nokogiri-1.19.0.gem
</code></pre>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/sparklemotion/nokogiri/blob/main/CHANGELOG.md">nokogiri's
changelog</a>.</em></p>
<blockquote>
<h2>v1.19.1 / 2026-02-16</h2>
<h3>Security</h3>
<ul>
<li>[CRuby] Address unchecked return value from
<code>xmlC14NExecute</code> which was a contributing cause to ruby-saml
GHSA-x4h9-gwv3-r4m4. See <a
href="https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-wx95-c6cv-8532">GHSA-wx95-c6cv-8532</a>
for more information.</li>
</ul>
<h2>v1.19.0 / 2025-12-28</h2>
<h4>Ruby</h4>
<p>This release is focused on changes to Ruby version support, and is
otherwise functionally identical to v1.18.10.</p>
<ul>
<li>Introduce native gem support for Ruby 4.0. <a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/3590">#3590</a></li>
<li>End support for Ruby 3.1, for which <a
href="https://www.ruby-lang.org/en/downloads/branches/">upstream support
ended 2025-03-26</a>.</li>
<li>End support for JRuby 9.4 (which targets Ruby 3.1
compatibility).</li>
</ul>
<h2>v1.18.10 / 2025-09-15</h2>
<h3>Dependencies</h3>
<ul>
<li>[CRuby] Vendored libxml2 is updated to <a
href="https://gitlab.gnome.org/GNOME/libxml2/-/releases/v2.13.9">v2.13.9</a>.
Note that the security fixes published in v2.13.9 were already present
in Nokogiri v1.18.9.</li>
<li>[CRuby] [Windows and MacOS] Vendored libiconv is updated to <a
href="https://savannah.gnu.org/news/?id=10703">v1.18</a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/sparklemotion/nokogiri/commit/d9130457369de8a6efcb764e6da2cb80d5d3b6dd"><code>d913045</code></a>
version bump to v1.19.1</li>
<li><a
href="https://github.com/sparklemotion/nokogiri/commit/b81cb9869e8ed7d1785da3363ef490f455da96eb"><code>b81cb98</code></a>
doc: update CHANGELOG for upcoming v1.19.1</li>
<li><a
href="https://github.com/sparklemotion/nokogiri/commit/8e668095c6147def4a3ec044df5f2a478c8161c3"><code>8e66809</code></a>
C14n raise on failure (<a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/3600">#3600</a>)</li>
<li><a
href="https://github.com/sparklemotion/nokogiri/commit/5b77f3d1c48cc09c92d10046c448a0866380eb4a"><code>5b77f3d</code></a>
Raise RuntimeError when canonicalization fails</li>
<li><a
href="https://github.com/sparklemotion/nokogiri/commit/edc559565819459d92f6db609f068f50491a57f9"><code>edc5595</code></a>
Thank sponsors in the README</li>
<li><a
href="https://github.com/sparklemotion/nokogiri/commit/d4dc245dfafd7ba42538051b0979306c8e5dc6f2"><code>d4dc245</code></a>
dep: update rdoc to v7</li>
<li><a
href="https://github.com/sparklemotion/nokogiri/commit/d77bfb66302532b90c0f340ed6b4ae74f275dde8"><code>d77bfb6</code></a>
version bump to v1.19.0</li>
<li><a
href="https://github.com/sparklemotion/nokogiri/commit/1eb5c2c035b360fd1195de0b274e901b6e0c12dd"><code>1eb5c2c</code></a>
dev: convert scripts/test-gem-set to use mise</li>
<li><a
href="https://github.com/sparklemotion/nokogiri/commit/88a120fd8198cd49b7cbe6388c92cd92d776407d"><code>88a120f</code></a>
dep: Add native Ruby 4 support, drop Ruby 3.1 support (v1.19.x) (<a
href="https://redirect.github.com/sparklemotion/nokogiri/issues/3592">#3592</a>)</li>
<li><a
href="https://github.com/sparklemotion/nokogiri/commit/f8c8f74e846ea49d2cb221710cc08618842ba21e"><code>f8c8f74</code></a>
Skip the parser compression test for Windows system libs</li>
<li>Additional commits viewable in <a
href="https://github.com/sparklemotion/nokogiri/compare/v1.18.9...v1.19.1">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/chatwoot/chatwoot/network/alerts).
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
# Pull Request Template
## Description
This PR updates settings page UI
## 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
## How to reproduce
In Chatwoot Cloud, mark an account for deletion from account settings
while the account has an active Stripe subscription. Before this change,
deletion marking did not explicitly mark subscriptions to stop renewing
at period end.
## What changed
This PR adds `Enterprise::Billing::CancelCloudSubscriptionsService` and
calls it from the delete action path in
`Enterprise::Api::V1::AccountsController`. The service lists only active
Stripe subscriptions for the customer and sets `cancel_at_period_end:
true` when needed. The account deletion schedule remains unchanged
(existing static 7-day behavior), and Stripe deleted-event fallback
behavior remains unchanged.
## How this was tested
Added and updated specs:
-
`spec/enterprise/services/enterprise/billing/cancel_cloud_subscriptions_service_spec.rb`
-
`spec/enterprise/controllers/enterprise/api/v1/accounts_controller_spec.rb`
Executed:
- `bundle exec rspec
spec/enterprise/services/enterprise/billing/cancel_cloud_subscriptions_service_spec.rb`
- `bundle exec rspec
spec/enterprise/controllers/enterprise/api/v1/accounts_controller_spec.rb:363`
Adds a Call model to track voice call state across providers (Twilio,
WhatsApp). This replaces storing call data in
conversation.additional_attributes and provides a foundation for call
analytics multi-call-per-conversation support, and future voice
providers.
---------
Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com>
## Description
ConversationReplyMailer#parse_email calls
Mail::Address.new(email_string).address without error handling. When an
account's support_email contains a non-email string (e.g., "Smith
Smith"), the mail gem raises Mail::Field::IncompleteParseError, crashing
conversation transcript emails.
This has caused 1,056 errors on Sentry (EXTERNAL-CHATINC-JX) since Feb
25, all from a single account that has a name stored in the
support_email field instead of a valid email address.
Closes
https://linear.app/chatwoot/issue/CW-6687/mailfieldincompleteparseerror-mailaddresslist-can-not-parse-orsmith
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
## 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: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Vishnu Narayanan <iamwishnu@gmail.com>
## Linear ticket
https://linear.app/chatwoot/issue/CW-6834/billing-upgrade-didnt-work
## Description
A `customer.subscription.updated` Stripe webhook for account 76162
returned 200 OK but did not persist the new `subscribed_quantity`. Root
cause: a race condition between the webhook handler and
`increment_response_usage` (Captain usage counter), both doing
read-modify-write on the `custom_attributes` JSONB column. The webhook
wrote `quantity: 6`, then a concurrent `save` from
`increment_response_usage` overwrote the entire hash with stale data —
restoring `quantity: 5`.
Fix: use atomic `jsonb_set` so usage counter updates only touch the
single key they care about, instead of rewriting the whole
`custom_attributes` hash. `increment_custom_attribute` also performs the
increment in SQL, making concurrent increments correct as well.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
- New regression spec in `handle_stripe_event_service_spec.rb` that
simulates concurrent webhook + `increment_response_usage` and asserts
both `subscribed_quantity` and `captain_responses_usage` survive
- Existing account, billing, captain, and topup specs all pass locally
## 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
Label attach/detach against a shared label no longer deadlocks under
parallel load. During high-concurrency label writes (for example, a
broadcast script attaching a campaign label to many conversations at
once), Chatwoot previously hit periodic `ActiveRecord::Deadlocked`
errors and tail-latency spikes on the tags table. This PR removes the
contention by disabling the `acts-as-taggable-on` counter cache, which
Chatwoot never reads.
## Closes
Fixes [INF-68](https://linear.app/chatwoot/issue/INF-68) (event 2)
## How to reproduce
1. Seed an account with ~20 conversations and 5 labels.
2. Spawn 20 parallel threads, each calling
`conversation.update!(label_list: shared_labels.shuffle)` against
different conversations.
3. Observe `ActiveRecord::Deadlocked` exceptions and p99 label-write
latency well above 1s.
With the counter cache disabled, the deadlock cycle cannot form.
## How this was tested
- Ran a 20-thread synthetic load test locally, each thread attaching 5
shared labels (shuffled per request) to different conversations. With
the counter cache enabled: 8 deadlocks across 300 attempts, p99 ~2.2s.
With the counter cache disabled: zero deadlocks, p99 ~306ms (roughly 85%
tail-latency reduction). The `UPDATE tags SET taggings_count = ...`
statement disappears from the SQL log entirely.
- Verified at boot via `rails runner` that
`ActsAsTaggableOn::Tagging.reflect_on_association(:tag).options[:counter_cache]`
returns `false` after the initializer runs. The gem wires `belongs_to
:tag, counter_cache: ActsAsTaggableOn.tags_counter` at class-load time,
so the initializer must sit ahead of the `Tagging` autoload path; this
confirms it does.
## Description
The IMAP email fetch job (Inboxes::FetchImapEmailsJob) crashes with an
unhandled IOError: closed stream when the mail server's SSL socket is
closed mid-write during Net::IMAP#fetch. This error was being reported
to Sentry because the rescue clause only caught EOFError, not its parent
class IOError.
Fixes
[CW-6689](https://linear.app/chatwoot/issue/CW-6689/ioerror-closed-stream-ioerror)
Widened the rescue in fetch_imap_emails_job.rb from EOFError to IOError.
In Ruby's exception hierarchy, EOFError is a subclass of IOError:
```
StandardError
└── IOError
└── EOFError
```
The Sentry stacktrace shows a plain IOError: closed stream raised from
OpenSSL::Buffering#do_write → Net::IMAP#put_string → Net::IMAP#fetch.
Since this is an IOError (not EOFError), it bypassed the existing rescue
and fell through to the StandardError catch-all, which reported it to
Sentry as an unhandled exception.
Rescuing IOError now catches both:
IOError: closed stream — the reported crash (parent class)
EOFError — the previously handled case (still caught as a subclass)
## Type of change
- [ ] Bug fix (non-breaking change which fixes an issue)
## 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: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Routing errors (404s) are expected in production and don't represent
actionable issues. Reporting them to New Relic creates noise and makes
it harder to spot real errors. Adds ActionController::RoutingError to
the New Relic error_collector.ignore_errors list so these are no longer
tracked as exceptions.
Removes sentry flooding of unnecessary rubyllm logs of wrong API key.
Logs only system api key error since it would be P0.
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## Description
Enable assignment v2 by default for new accounts
## 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 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
HandoffTool changes conversation status but only posts a private note.
ResponseBuilderJob now detects the tool flag and creates the public
handoff message that was previously only shown in V1.
# Pull Request Template
## Description
Captain V2 was silently forwarding conversations to humans without
showing a handoff message to the customer. The conversation appeared to
just stop
responding.
Root cause: In V2, HandoffTool calls bot_handoff! during agent
execution, which changes conversation status from pending to open. By
the time control returns
to ResponseBuilderJob#process_response, the conversation_pending? guard
returns early - skipping create_handoff_message entirely. The V1 flow
didn't have this
problem because AssistantChatService just returns a string token
(conversation_handoff) and lets ResponseBuilderJob handle everything.
What changed:
1. AgentRunnerService now surfaces the handoff_tool_called flag (already
tracked internally for usage metadata) in its response hash.
2. ResponseBuilderJob#handoff_requested? detects handoffs from both V1
(response token) and V2 (tool flag).
3. ResponseBuilderJob#process_response checks handoff_requested? before
the conversation_pending? guard, so V2 handoffs are processed even when
the status has
already changed.
4. ResponseBuilderJob#process_action('handoff') captures
conversation_pending? before calling bot_handoff! and uses that snapshot
to guard both bot_handoff!
and the OOO message - preventing double-execution when V2's HandoffTool
already ran them.
New V2 handoff flow:
AgentRunnerService
→ agent calls HandoffTool (creates private note, calls bot_handoff!)
→ returns response with handoff_tool_called: true
ResponseBuilderJob#process_response
→ handoff_requested? detects the flag
→ process_action('handoff')
→ create_handoff_message (public message for customer)
→ bot_handoff! skipped (conversation_pending? is false)
→ OOO skipped (conversation_pending? is false)
Fixes#13881
## 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?
- Update existing response_builder_job_spec.rb covering the V2 handoff
path, V2 normal response path, and V1 regression
- Updated existing agent_runner_service_spec.rb expectations for the new
handoff_tool_called key and added a context for when the flag is true
## 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
- [x] Any dependent changes have been merged and published in downstream
modules
---------
Co-authored-by: Aakash Bakhle <48802744+aakashb95@users.noreply.github.com>
Co-authored-by: aakashb95 <aakashbakhle@gmail.com>
Comprehensive update to Traditional Chinese (Taiwan) translations. As a
native zh-TW speaker and active user based in Taiwan, I found the
existing translations were quite incomplete (~54% overall) with many
strings still in English. Some existing translations also used
Simplified Chinese terms or unnatural phrasing.
I chose to submit this as a direct PR rather than going through Crowdin
because working through all the files at once is much faster and lets me
ensure consistent terminology across the entire locale.
Closes#14003
## What changed
**Backend (`config/locales/zh_TW.yml`)**
- Translated all ~259 previously untranslated strings (was ~19%
complete, now 100%)
- Covers: error messages, notifications, activity logs, integration
descriptions, Captain AI, public portal, reports
**Frontend (42 JSON files under `dashboard/i18n/locale/zh_TW/`)**
- Translated ~2,627 previously untranslated strings (was ~50% complete,
now ~100%)
- Most impacted files: `inboxMgmt.json`, `integrations.json`,
`settings.json`, `conversation.json`, `contact.json`, `report.json`
**Quality fixes across all files**
- Replaced Simplified Chinese terms mixed into zh-TW: 账→帳, 获→取得, 模板→範本,
收件箱→收件匣, 重置→重設, 自定義→自訂
- Standardized terminology for consistency: 客服人員 (agent), 延後 (snooze),
稽核 (audit), 巨集 (macro)
- Fixed incorrect translations (e.g., audit log table headers were
swapped, availability label was wrong)
## How to test
1. Set account/user language to 中文(台灣)
2. Navigate through the dashboard — settings, inbox management,
integrations, reports, conversations
3. Verify strings display in natural Traditional Chinese with no
remaining English gaps
4. Check that all placeholders (names, counts, dates) render correctly
# Pull Request Template
## Description
This PR includes, block inline images in message signatures and prevent
auto signature insertion when editor is disabled.
- Strip inline base64 images from signature on save and show warning
message
- Add `INLINE_IMAGE_WARNING` translation key for signature inline image
removal notification
- Add disabled check to `addSignature()` to prevent signature insertion
when editor is disabled
- Add `isEditorDisabled` checks to signature toggle logic in
`toggleSignatureForDraft()`, `replaceText()`, and `clearMessage()`
- Remove unused `replaceText` from the codebase, which belongs to old
`textarea` editor
Fixes
https://linear.app/chatwoot/issue/CW-6588/the-browser-hangs-when-the-message-signature-contains-inline-image
## 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/fb556b46a12a4308a737eed732d5ed73
## 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
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
## Account branding enrichment during signup
This PR does the following
### Replace Firecrawl with Context.dev
Switches the enterprise brand lookup from Firecrawl to Context.dev for
better data quality, built-in caching, and automatic filtering of
free/disposable email providers. The service interface changes from URL
to email input to match Context.dev's email endpoint. OSS still falls
back to basic HTML scraping with a normalized output shape across both
paths.
The enterprise path intentionally does not fall back to HTML scraping on
failure — speed matters more than completeness. We want the user on the
editable onboarding form fast, and a slow fallback scrape is worse than
letting them fill it in.
Requires `CONTEXT_DEV_API_KEY` in Super Admin → App Config. Without it,
falls back to OSS HTML scraping.
### Add job to enrich account details
After account creation, `Account::BrandingEnrichmentJob` looks up the
signup email and pre-fills the account name, colors, logos, social
links, and industry into `custom_attributes['brand_info']`.
The job signals completion via a short-lived Redis key (30s TTL) + an
ActionCable broadcast (`account.enrichment_completed`). The Redis key
lets the frontend distinguish "still running" from "finished with no
results."
Previously, signing up gave immediate access to the app. Now,
unconfirmed users are redirected to a verification page where they can
resend the confirmation email.
- After signup, the user is routed to `/auth/verify-email` instead of
the dashboard
- After login, unconfirmed users are redirected to the verification page
- The dashboard route guard catches unconfirmed users and redirects them
- `active_for_authentication?` is removed from the sessions controller
so unconfirmed users can authenticate — the frontend gates access
instead
- If the user visits the verification page after already confirming,
they're automatically redirected to the dashboard
- No session is issued until the user is verified
<details><summary>Demo</summary>
<p>
#### Fresh Signup
https://github.com/user-attachments/assets/abb735e5-7c8e-44a2-801c-96d9e4823e51
#### Google Fresh Signup
https://github.com/user-attachments/assets/ab9e389a-a604-4a9d-b492-219e6d94ee3f
#### Create new account from Dashboard
https://github.com/user-attachments/assets/c456690d-1946-4e0b-834b-ad8efcea8369
</p>
</details>
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
# Pull Request Template
## Description
Custom tools is now discoverable on all plans
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Before:
<img width="390" height="446" alt="CleanShot 2026-04-02 at 13 40 11@2x"
src="https://github.com/user-attachments/assets/0a751954-f3ad-47d6-85b8-1e2f1476a646"
/>
After:
<img width="392" height="522" alt="CleanShot 2026-04-02 at 13 40 47@2x"
src="https://github.com/user-attachments/assets/62a252f6-2551-47a9-b50c-be949f08c456"
/>
<img width="1826" height="638" alt="CleanShot 2026-04-02 at 13 37 39@2x"
src="https://github.com/user-attachments/assets/77dc2a75-3d76-44cf-8579-8d3457879bd0"
/>
## 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
- [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
- [x] Any dependent changes have been merged and published in downstream
modules
---------
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
Fixes failing `agent_bot_listener_spec.rb` tests for
`conversation_status_changed` events. After #13892 added webhook
signing, `process_webhook_bot_event` passes `:agent_bot_webhook` and
`secret:`/`delivery_id:` kwargs to
`AgentBots::WebhookJob.perform_later`, but two spec expectations were
not updated to match the new call signature.
## What changed
- Updated `perform_later` expectations in `conversation_status_changed`
specs to include the `:agent_bot_webhook` type and `secret` keyword
arguments.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Agent bots assigned to a conversation were not receiving
`conversation_status_changed` webhook events. This meant bots could not
react to status transitions like moving a conversation to **pending**.
The deprecated `conversation_opened` and `conversation_resolved` events
were still being delivered, but the newer unified
`conversation_status_changed` event was silently dropped because
`AgentBotListener` had no handler for it.
## What changed
- Added `conversation_status_changed` handler to `AgentBotListener`,
matching the pattern already used by `WebhookListener`. The payload
includes `changed_attributes` so bots know which status transition
occurred.
## How to test
1. Configure an agent bot with an `outgoing_url` (e.g. a webhook.site
endpoint).
2. Assign the bot to an inbox or conversation.
3. Change a conversation's status to **pending** (or any other status).
4. Verify the bot receives a `conversation_status_changed` event with
the correct `changed_attributes`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Account webhooks sign outgoing payloads with HMAC-SHA256, but agent bot
and API inbox webhooks were delivered unsigned. This PR adds the same
signing to both.
Each model gets a dedicated `secret` column rather than reusing the
agent bot's `access_token` (for API auth back into Chatwoot) or the API
inbox's `hmac_token` (for inbound contact identity verification). These
serve different trust boundaries and shouldn't be coupled — rotating a
signing secret shouldn't invalidate API access or contact verification.
The existing `Webhooks::Trigger` already signs when a secret is present,
so the backend change is just passing `secret:` through to the jobs.
Shared token logic is extracted into a `WebhookSecretable` concern
included by `Webhook`, `AgentBot`, and `Channel::Api`. The frontend
reuses the existing `AccessToken` component for secret display. Secrets
are admin-only and excluded from enterprise audit logs.
### How to test
Point an agent bot or API inbox webhook URL at a request inspector. Send
a message and verify `X-Chatwoot-Signature` and `X-Chatwoot-Timestamp`
headers are present. Reset the secret from settings and confirm
subsequent deliveries use the new value.
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
The `conversation_updated` webhook sent to AgentBots did not include
`changed_attributes`, making it impossible for bots to distinguish
between different types of conversation updates (e.g. bot assignment vs
label change vs status change).
This aligns the AgentBot webhook payload with the existing
`WebhookListener` behavior, which already includes `changed_attributes`.
## How to reproduce
1. Assign an AgentBot to a conversation
2. Then update the conversation (e.g. add a label)
3. **Before fix:** Both events arrive with identical payload structure —
bot cannot tell them apart
4. **After fix:** Each event includes `changed_attributes` showing
exactly what changed
## What changed
- **`AgentBotListener#conversation_updated`**: Added
`changed_attributes` to the webhook payload using
`extract_changed_attributes` (same pattern as `WebhookListener`)
## How to test
1. Assign an AgentBot to a conversation via API
2. Check the webhook payload — should include:
```json
"changed_attributes": [
{ "assignee_agent_bot_id": { "previous_value": null, "current_value": 7
} }
]
```
3. Update the conversation (e.g. add a label)
4. Check the webhook payload — `changed_attributes` should reflect the
label change, not bot assignment
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## Description
Two improvements to Agent Capacity Policy:
**1. Support exclusion via zero conversation limit**
Allow `conversation_limit` to be `0` on inbox capacity limits. Agents
with a zero limit are excluded from auto-assignment for that inbox while
remaining members for manual assignment.
**2. Fix exclusion rules duration input**
- Default changed from `10` to `null` so time-based exclusion isn't
applied unless explicitly set.
- Minimum lowered from 10 to 1 minute.
- `DurationInput` updated to handle `null` values correctly.
## 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)
## How Has This Been Tested?
- Added model and capacity service specs for zero-limit exclusion
behavior.
- Tested manually via UI flows
## 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: Muhsin Keloth <muhsinkeramam@gmail.com>
## Summary
When a Super Admin creates a new account via the Administrate dashboard,
the `manually_managed_features` field (a virtual attribute stored in
`internal_attributes` JSON) is passed to `Account.new(...)`, raising
`ActiveModel::UnknownAttributeError`. The existing `update` action
already strips this param — this fix adds the same handling to `create`.
Closes -> https://linear.app/chatwoot/issue/INF-66
Related Sentry ->
https://chatwoot-p3.sentry.io/issues/7168237533/?project=6382945&referrer=Linear
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How to reproduce
1. Log in as Super Admin
2. Navigate to Accounts → New
3. Fill in the form (with or without manually managed features selected)
4. Submit → `ActiveModel::UnknownAttributeError: unknown attribute
'manually_managed_features' for Account`
## What changed
- Added a `create` override in
`Enterprise::SuperAdmin::AccountsController` that strips
`manually_managed_features` from params before calling `super`, then
persists them via `InternalAttributesService` after the account is
saved.
Previously, all incoming messages from Facebook channel with
instagram_id had their attachment data_url and thumb_url overridden with
external_url. This caused issues for non-Instagram conversations
originating from Facebook Message where the file URL should be used
instead.
Narrows the override to only apply when the conversation type is
instagram_direct_message, which is the only case where Instagram's CDN
URLs need to be used directly.
Fixes
https://linear.app/chatwoot/issue/CW-6722/videos-are-missing-in-facebook-conversation
---------
Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When an AgentBot is assigned to a conversation after the first message
has already been received, the bot does not respond because it never
receives any event. The `message_created` event fires before the bot is
assigned, and the bot has no way to know it was assigned.
Chatwoot already dispatches a `CONVERSATION_UPDATED` event when
`assignee_agent_bot_id` changes, but `AgentBotListener` wasn't listening
for it. This fix adds a `conversation_updated` handler so the bot
receives a webhook with the conversation context when assigned.
## How to reproduce
1. Customer sends a message → conversation created, `message_created`
fires
2. System processes the message (adds labels, custom attributes)
3. System assigns an AgentBot to the conversation via API
4. **Before fix:** Bot receives no event and never responds
5. **After fix:** Bot receives `conversation_updated` event with
conversation payload
## What changed
- **`AgentBotListener`**: Added `conversation_updated` handler that
sends the conversation webhook payload to the assigned bot when the
conversation is updated
## How to test
1. Create an AgentBot with an `outgoing_url` pointing to a webhook
inspector (e.g. webhook.site)
2. Send a message to create a conversation
3. Assign the AgentBot to the conversation via API:
```
POST /api/v1/accounts/{id}/conversations/{id}/assignments
{ "assignee_id": <bot_id>, "assignee_type": "AgentBot" }
```
4. Verify the bot receives a `conversation_updated` event at its webhook
URL
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
### Description
When integrating the web widget via the JS SDK, customers call
setConversationCustomAttributes and setLabel on chatwoot:ready — before
any conversation exists. These API calls silently fail because the
backend endpoints require an existing conversation. When the visitor
sends their first message, the conversation is created without those
attributes/labels, so the message_created webhook payload is missing the
expected metadata.
This change queues SDK-set conversation custom attributes and labels in
the widget store when no conversation exists yet, and includes them in
the API request when the first message (or attachment) creates the
conversation. The backend now permits and applies these params during
conversation creation — before the message is saved and webhooks fire.
### How to test
1. Configure a web widget without a pre-chat form.
2. Open the widget on a test page and run the following in the browser
console after chatwoot:ready:
`window.$chatwoot.setConversationCustomAttributes({ plan: 'enterprise'
});`
`window.$chatwoot.setLabel('vip');` // must be a label that exists in
the account
3. Send the first message from the widget.
4. Verify in the Chatwoot dashboard that the conversation has plan:
enterprise in custom attributes and the vip label applied.
5. Set up a webhook subscriber for `message_created` confirm the first
payload includes the conversation metadata.
6. Verify that calling `setConversationCustomAttributes` / `setLabel` on
an existing conversation still works as before (direct API path, no
regression).
7. Verify the pre-chat form flow still works as expected.
Attachment webhook event payloads (`message_created`) were missing the
file extension and content type. The `extension` column existed but was
never populated, and `content_type` was not included in the payload at
all.
## What changed
- Added `before_save :set_extension` callback to extract file extension
from the filename when saving an attachment.
- Added `content_type` (from ActiveStorage) to the `file_metadata` used
in `push_event_data`.
### Before
```json
{
"extension": null,
"data_url": "...",
"file_size": 11960
}
```
### After
```json
{
"extension": "pdf",
"content_type": "application/pdf",
"data_url": "...",
"file_size": 11960
}
```
## How to reproduce
1. Send a message with a file attachment (e.g., PDF) via any channel
2. Inspect the `message_created` webhook payload
3. Observe `extension` is `null` and `content_type` is missing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
# Pull Request Template
## Description
Adds custom tool support to v1
## Type of change
- [x] New feature (non-breaking change which adds functionality)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
<img width="1816" height="958" alt="CleanShot 2026-03-24 at 11 37 33@2x"
src="https://github.com/user-attachments/assets/2777a953-8b65-4a2d-88ec-39f395b3fb47"
/>
<img width="378" height="488" alt="CleanShot 2026-03-24 at 11 38 18@2x"
src="https://github.com/user-attachments/assets/f6973c99-efd0-40e4-90fe-4472a2f63cea"
/>
<img width="1884" height="1452" alt="CleanShot 2026-03-24 at 11 38
32@2x"
src="https://github.com/user-attachments/assets/9fba4fc4-0c33-46da-888a-52ec6bad6130"
/>
## Checklist:
- [x] 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: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
When a user signs up with an email they don't own and sets a password,
that password remains valid even after the real owner later signs in via
OAuth. This means the original registrant — who never proved ownership
of the email — retains working credentials on the account. This change
closes that gap by rotating the password to a random value whenever an
unconfirmed user completes an OAuth sign-in.
The check (`oauth_user_needs_password_reset?`) is evaluated before
`skip_confirmation!` runs, since confirmation would flip `confirmed_at`
and mask the condition. If the user was unconfirmed, the stored password
is replaced with a secure random string that satisfies the password
policy. This applies to both the web and mobile OAuth callback paths, as
well as the sign-up path where the password is rotated before the reset
token is generated.
Users who lose access to password-based login as a side effect can
recover through the standard "Forgot password" flow at any time. Since
they've already proven email ownership via OAuth, this is a low-friction
recovery path
# Pull Request Template
## Description
This PR fixes an issue where markdown tables were not rendering
correctly in the Help Center.
The issue was caused by a backslash `(\)` being appended after table row
separators `(|)`, which breaks the markdown table parsing.
The issue was introduced after recent editor changes made to preserve
new lines, which unintentionally affected how table markdown is parsed
and displayed.
### https://github.com/chatwoot/prosemirror-schema/pull/44
Fixes
https://linear.app/chatwoot/issue/CW-6714/markdown-tables-dont-render-properly-in-help-centre-preview
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
**Before**
```
| Type | What you provide |\
|--------------|-------------------------------|\
| None | No authentication |\
| Bearer Token | A token string |\
| Basic Auth | Username and password |\
| API Key | A custom header name and value|
```
**After**
```
| Type | What you provide |
|--------------|-------------------------------|
| None | No authentication |
| Bearer Token | A token string |
| Basic Auth | Username and password |
| API Key | A custom header name and value|
```
## 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
## Summary
- Add `MutexApplicationJob::LockAcquisitionError` to Sentry's
`excluded_exceptions`
- This error is expected control flow (mutex lock contention during
webhook processing), not a bug
- Generated ~131K Sentry events in March 2026, 100% from
`InstagramEventsJob`
Fixes https://linear.app/chatwoot/issue/INF-58
Images and videos sent from Chatwoot to LINE inboxes fail to display on
the LINE mobile app — users see expired markers, broken thumbnails, or
missing images. This happens because LINE mobile lazy-loads images
rather than downloading them immediately, and the ActiveStorage signed
URLs expire after 5 minutes.
Closes
https://linear.app/chatwoot/issue/CW-6696/line-messaging-with-image-or-video-may-not-show-when-client-inactive
## How to reproduce
1. Create a LINE inbox and start a chat from the LINE mobile app
2. Close the LINE mobile app
3. Send an image from Chatwoot to that chat
4. Wait 7-8 minutes (past the 5-minute URL expiration)
5. Open the LINE mobile app — the image is broken/expired
## What changed
- **`originalContentUrl`**: switched from `download_url` (signed, 5-min
expiry) to `file_url` (permanent redirect-based URL)
- **`previewImageUrl`**: switched to `thumb_url` (250px resized
thumbnail meeting LINE's 1MB/240x240 recommendation), with fallback to
`file_url` for non-image attachments like video
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Sojan Jose <sojan@pepalo.com>
## Linear Ticket
https://linear.app/chatwoot/issue/CW-6707/socketresolutionerror-failed-to-open-tcp-connection-to-permanentlyhttps://linear.app/chatwoot/issue/CW-6707/socketresolutionerror-failed-to-open-tcp-connection-to-permanently#comment-14e0f9ff
## Description
Browser push notifications fail with Socket::ResolutionError when the
push subscription endpoint's domain can't be resolved via DNS (e.g.,
defunct push service, transient DNS failure). This error wasn't handled
in handle_browser_push_error, so it fell through to the catch-all else
branch and got reported to Sentry on every notification attempt — 1,637
times in the last 7 days.
The dead subscription was never cleaned up or the error suppressed, so
every subsequent notification for the affected user triggered the same
Sentry alert.
Added Socket::ResolutionError to the existing transient network error
handler alongside Errno::ECONNRESET, Net::OpenTimeout, and
Net::ReadTimeout. The error is logged but not reported to Sentry, and
the subscription is kept intact in case it's a temporary DNS blip.
## Type of change
- [ ] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
- Verified that Socket::ResolutionError is a subclass of StandardError
and matches the when clause
## 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: Vishnu Narayanan <iamwishnu@gmail.com>
# Pull Request Template
## Description
This PR fixes the white background bleed visible in the widget, widget
article viewer and help center when dark mode is active.
**What was happening**
While scrolling, the `<body>` element retained a white background in
dark mode. This occurred because dark mode classes were only applied to
inner container elements, not the root.
**What changed**
* **Widget:** Updated the `useDarkMode` composable to sync the `dark`
class to `<html>` using `watchEffect`, allowing `<body>` to inherit dark
theme variables. Also added background styles to `html`, `body`, and
`#app` in `woot.scss`.
* **Help center portal:** Moved `bg-white dark:bg-slate-900` from
`<main>` to `<body>` in the portal layout so the entire page background
responds correctly to dark mode, including within the widget iframe.
* **ArticleViewer:** Replaced hardcoded `bg-white` with `bg-n-solid-1`
to ensure better theming.
Fixes
https://linear.app/chatwoot/issue/CW-6704/widget-body-colour-not-implemented
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
### Screencasts
### Before
**Widget**
https://github.com/user-attachments/assets/e0224ad1-81a6-440a-a824-e115fb806728
**Help center**
https://github.com/user-attachments/assets/40a8ded5-5360-474d-9ec5-fd23e037c845
### After
**Widget**
https://github.com/user-attachments/assets/dd37cc68-99fc-4d60-b2ae-cf41f9d4d38c
**Help center**
https://github.com/user-attachments/assets/bc998c4e-ef77-46fa-ac7f-4ea16d912ce3
## 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
The initial version of prompt deciding to resolve or hand-off to human
agents was too conservative especially in cases where a link or an
action was told to customer. If the customer didn't respond, Captain was
told to hand it off to the agent, but customer may actually have solved
the issue. If not, they can come back and continue the conversation.
Removed two lines about the same and now we should not see needless
handoffs.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
locally
## 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
- [x] Any dependent changes have been merged and published in downstream
modules
# Pull Request Template
## Description
This PR fixes
1. Messages being trimmed to the default 1024 limit in `trimContent`
method, instead of channel-specific limits for drafts and AI tasks.
2. Telegram messages are allowed up to 10,000 characters in config, but
the API supports only 4096, causing failures for oversized messages.
Fixes
https://linear.app/chatwoot/issue/CW-6694/captain-ai-rewrite-tasks-truncate-draft-to-1024-chars-trimcontenthttps://github.com/chatwoot/chatwoot/issues/13919
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
### Loom video
**Before**
https://www.loom.com/share/00e9d6b4d19247febf35dffa99da3805
**After**
https://www.loom.com/share/c4900e9effc345c79bcd8a5aa1ee277b
## 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
Repurpose the deprecated response_bot feature flag slot for
custom_tools.
Migration disables the flag on any accounts that had response_bot
enabled so the repurposed slot starts in its default-off state.
Pre-deploy: run the disable script on production using the old flag name
(response_bot) before deploying this migration.
Adds `WebsiteBrandingService` (OSS) with an Enterprise override using
Firecrawl v2 to extract branding and business data from a URL for
onboarding auto-fill.
OSS version uses HTTParty + Nokogiri to extract:
- Business name (og:site_name or title)
- Language (html lang)
- Favicon
- Social links from `<a>` tags
Enterprise version makes a single Firecrawl call to fetch:
- Structured JSON (name, language, industry via LLM)
- Branding (favicon, primary color)
- Page links
Falls back to OSS if Firecrawl is unavailable or fails.
Social handles (WhatsApp, Facebook, Instagram, Telegram, TikTok, LINE)
are parsed deterministically via a shared `SocialLinkParser`.
> We use links for socials, since the LLM extraction was unreliable,
mostly returned empty, and hallucinated in some rare scenarios
## How to test
```ruby
# OSS (no Firecrawl key needed)
WebsiteBrandingService.new('chatwoot.com').perform
# Enterprise (requires CAPTAIN_FIRECRAWL_API_KEY)
WebsiteBrandingService.new('notion.so').perform
WebsiteBrandingService.new('postman.com').perform
```
Verify the returned hash includes business_name, language,
industry_category, social_handles, and branding with
favicon/primary_color.
<img width="908" height="393" alt="image"
src="https://github.com/user-attachments/assets/e3696887-d366-485a-89a0-8e1a9698a788"
/>
## Description
When a customer downgrades from Enterprise to Business, they may retain
unused Stripe credit balance. During an AI credits topup,
Stripe::Invoice.finalize_invoice auto-applies that credit balance to the
invoice. If the credit balance fully covers the invoice amount, Stripe
marks it as paid immediately upon finalization. Calling
Stripe::Invoice.pay on an already-paid invoice throws an error, breaking
the topup flow.
This fix retrieves the invoice status after finalization and skips the
pay call if Stripe has already settled it via credits.
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Tested against Stripe test mode with the following scenarios:
- Full credit balance payment: Customer has enough Stripe credit balance
to cover the entire invoice. Invoice is marked paid after
finalize_invoice — pay is correctly skipped. Credits are fulfilled
successfully.
- Partial credit balance payment: Customer has some Stripe credit
balance but not enough to cover the full amount. Invoice remains open
after finalization — pay is called and charges the remaining amount to
the default payment method. Credits are fulfilled successfully.
- Zero credit balance (normal payment): Customer has no Stripe credit
balance. Invoice remains open after finalization — pay charges the full
amount. Credits are fulfilled successfully.
## 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
Adds Estonian to the settings language dropdown so accounts can select
the existing `et` translation from the UI.
Fixes: N/A
Closes: N/A
## Why
Estonian translation files already exist in the repo and in Crowdin, but
the settings dropdown is driven by `LANGUAGES_CONFIG`, where `et` was
missing.
## What this change does
- Adds `et` / `Eesti (et)` to `LANGUAGES_CONFIG`
- Makes Estonian available in the settings language selectors backed by
`enabledLanguages`
## Validation
- `ruby -c config/initializers/languages.rb`
- Opened the local UI at `/app/accounts/1/settings/general` and verified
`Eesti (et)` appears in the `Site language` dropdown
---------
Co-authored-by: Pranav <pranav@chatwoot.com>
## Description
When Assignment V2 is enabled, the V2 capacity policies
(AgentCapacityPolicy / InboxCapacityLimit) are not respected during
team-based assignment paths. The system falls back to the legacy V1
max_assignment_limit, and since V1 is deprecated and typically
unconfigured in V2 setups, agents receive unlimited assignments
regardless of their V2 capacity.
Root cause: Inbox class directly defined
member_ids_with_assignment_capacity, which shadowed the
Enterprise::InboxAgentAvailability module override in Ruby's method
resolution order (MRO). This made the V2 capacity check unreachable
(dead code) for any code path using member_ids_with_assignment_capacity.
## Type of change
- [ ] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
⏺ Before the fix
1. Enable assignment_v2 + advanced_assignment on account
2. Create AgentCapacityPolicy with InboxCapacityLimit = 1 for an inbox
3. Assign the policy to an agent (e.g., John)
4. Create 1 open conversation assigned to John (now at capacity)
5. Create a new unassigned conversation in the same inbox
6. Assign a team (containing John) to that conversation
7. Result: John gets assigned despite being at capacity
⏺ After the fix
Same steps 1–6.
7. Result: John is NOT assigned — conversation stays unassigned (no
agents with capacity available)
## 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
This ignores `CVE-2026-33658` in `bundler-audit` after validating that
Chatwoot's default and recommended storage setups do not use Active
Storage proxy mode.
Fixes: N/A
Closes: N/A
## Why
`CVE-2026-33658` is an Active Storage proxy-mode DoS issue triggered by
multi-range requests.
For Chatwoot, the default and recommended setups do not appear to route
file downloads through Rails proxy mode:
- `config/environments/production.rb` selects the Active Storage service
but does not opt into `rails_storage_proxy`
- `.env.example` defaults to `ACTIVE_STORAGE_SERVICE=local`
- Chatwoot's storage docs recommend local/cloud storage with optional
direct uploads to the storage provider
- existing specs expect redirect/disk-style Active Storage URLs rather
than proxy-mode URLs
Given that validation, ignoring this advisory is a smaller and more
accurate response than a framework-wide Rails upgrade.
## What this change does
- adds `.bundler-audit.yml`
- preserves the existing advisory ignore entries already used by
Chatwoot
- ignores `CVE-2026-33658`
- documents why the ignore is acceptable for Chatwoot's current defaults
- notes that this should be revisited if Chatwoot enables
`rails_storage_proxy` or other app-served Active Storage proxy routes
## Validation
- reviewed `config/environments/production.rb`
- reviewed `.env.example`
- reviewed Chatwoot storage docs:
https://developers.chatwoot.com/self-hosted/deployment/storage/s3-bucket
- reviewed Active Storage URL expectations in
`spec/controllers/slack_uploads_controller_spec.rb` and
`spec/services/line/send_on_line_service_spec.rb`
- ran `bundle exec bundle-audit check --no-update`
This change blocks Help Center access for default/Hacker-plan accounts
and closes the downgrade gap that could leave `help_center` enabled
after a subscription falls back to the default cloud plan.
Fixes: none
Closes: none
## Why
Default-plan accounts should not be able to access the Help Center, but
the downgrade fallback path only reset the plan name and did not
reconcile premium feature flags. That meant some accounts could keep
`help_center` enabled even after landing back on the Hacker/default
plan.
## What this change does
- blocks Help Center portal and article access for default/Hacker-plan
accounts
- reconciles premium feature flags when a subscription falls back to the
default cloud plan, so `help_center` is disabled immediately instead of
waiting for a later webhook
- preserves existing account `custom_attributes` during Stripe customer
recreation instead of overwriting them
- adds Enterprise coverage for the default-plan access checks on hosted
and custom-domain Help Center routes
- fixes the public access check to use the resolved portal object so
blocked requests return the intended response instead of raising an
error
## Validation
1. Create or use an account on the default/Hacker cloud plan with an
active portal.
2. Visit the portal home page and a published article on both the
Chatwoot-hosted URL and a configured custom domain.
3. Confirm the Help Center is blocked for that account.
4. Downgrade a paid account back to the default/Hacker plan through the
Stripe webhook flow.
5. Confirm `help_center` is disabled right after the downgrade fallback
is processed and the account can no longer access the Help Center.
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Sojan Jose <sojan@pepalo.com>
# Pull Request Template
## Description
This PR upgrades the ProseMirror editor and enables automatic URL
linkification on paste. Previously, URLs were only linkified after a
user input event (e.g., typing a space). With this change, URLs are now
linkified instantly when pasted.
Fixes
https://linear.app/chatwoot/issue/CW-6682/email-channel-links-are-not-working
### https://github.com/chatwoot/prosemirror-schema/pull/42
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
**Screencast**
**Before**
https://github.com/user-attachments/assets/d38725c9-a152-4c2c-8c33-3ee717f1628f
**After**
https://github.com/user-attachments/assets/9a69a0b6-93ee-421e-896b-5a4e01a167ba
## 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 link formatting issue on the backend by adding
`:autolink` to `ChatwootMarkdownRenderer#render_message`, ensuring all
URLs are converted to `<a>` tags.
Fixes
https://linear.app/chatwoot/issue/CW-6682/email-channel-links-are-not-working
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## 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
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
issue: https://github.com/chatwoot/chatwoot/issues/13921
Fix a typo in the Messages API operationId where `converation` was used
instead of `conversation`. This causes cspell errors when generating
client code with tools like Orval.
## What changed
- `swagger/paths/public/inboxes/messages/index.yml`: fixed operationId
from `list-all-converation-messages` to `list-all-conversation-messages`
- `swagger/swagger.json` and `swagger/tag_groups/client_swagger.json`:
regenerated to reflect the fix
## Note
If you are using a code generator like Orval against this swagger spec,
the generated function name will change from
`listAllConverationMessages` to `listAllConversationMessages`.
Self-hosted installations were incorrectly hitting the daily email rate
limit of 100, seeded from `installation_config`. Since self-hosted users
control their own infrastructure, email rate limiting should only apply
to Chatwoot Cloud.
Closes#13913
After a successful WhatsApp OAuth reauthorization, the health check runs
immediately and finds the phone number in a pending provisioning state
(`platform_type: NOT_APPLICABLE`). This incorrectly triggers
`prompt_reauthorization!`, re-setting the Redis disconnect flag and
sending a disconnect email — even though the reauth just succeeded.
The fix skips the health check during reauthorization flows. It still
runs for new channel creation.
Closes https://github.com/chatwoot/chatwoot/pull/12556
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How to reproduce
1. Have a WhatsApp channel with a phone number in pending provisioning
state (display name not yet approved by Meta)
2. Complete the OAuth reauthorization flow
3. Observe that the user receives a "success" response but immediately
gets a disconnect email
## What changed
- `Whatsapp::EmbeddedSignupService#perform` now skips
`check_channel_health_and_prompt_reauth` when `inbox_id` is present
(reauthorization flow)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
When syncing contacts to LeadSquared, the `Lead.CreateOrUpdate` API
defaults to searching by email. If a contact has no email (or a
different email) but a phone number matching an existing lead, the API
fails with `MXDuplicateEntryException` instead of finding and updating
the existing lead. This accounted for ~69% of all LeadSquared
integration errors, and cascaded into "Lead not found" failures when
posting transcript and conversation activities (~14% of errors).
## What changed
- `LeadClient#create_or_update_lead` now catches
`MXDuplicateEntryException` and retries the request once with
`SearchBy=Phone` appended to the body, telling the API to match on phone
number instead
- Once the retry succeeds, the returned lead ID is stored on the contact
(existing behavior), so all future events use the direct `update_lead`
path and never hit the duplicate error again
## How to reproduce
1. Create a lead in LeadSquared with phone number `+91-75076767676` and
email `a@example.com`
2. In Chatwoot, create a contact with the same phone number but a
different email (or no email)
3. Trigger a contact sync (via conversation creation or contact update)
4. Before fix: `MXDuplicateEntryException` error in logs, contact fails
to sync
5. After fix: retry with `SearchBy=Phone` finds and updates the existing
lead, stores the lead ID on the contact
# Pull Request Template
## Description
Please include a summary of the change and issue(s) fixed. Also, mention
relevant motivation, context, and any dependencies that this change
requires.
- Add language_code setting to Dialogflow integration configuration
- Support 'auto' mode to detect language from contact's
additional_attributes
- Fallback to 'en-US' when no language is configured or detected
- Include comprehensive language options (22 languages)
- Add tests for language code configuration scenarios
Fixes#3071
## 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?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
<img width="815" height="506" alt="Screenshot 2026-01-10 220410"
src="https://github.com/user-attachments/assets/26d2619c-ed42-4c9a-a41d-9fb07ef91a30"
/>
## 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
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
This pull request fixes the model annotation tooling due to previous
incomplete migration from `annotate` to `annotaterb` gem (#12600). It
also improves the handling of serialized values in the
`InstallationConfig` model by ensuring a default value is set,
simplifying the code, and removing a workaround for YAML
deserialization.
**Annotation tooling updates:**
* Added `.annotaterb.yml` to configure the `annotate_rb` gem with
project-specific options, centralizing annotation settings.
* Replaced the custom `auto_annotate_models.rake` task with the standard
rake task from `annotate_rb`, and added `lib/tasks/annotate_rb.rake` to
load annotation tasks in development environments.
[[1]](diffhunk://#diff-9450d2359e45f1db407b3871dde787a25d60bb721aed179a65ffd2692e95fb4bL1-L61)
[[2]](diffhunk://#diff-578cdfc7ad56637e42472ea891ea286dff8803d9a1750afdbfeafec164d9b8b2R1-R8)
**Model serialization improvements:**
* Updated the `InstallationConfig` model to set a default value for the
`serialized_value` attribute, ensuring it always has a hash with
indifferent access and removing the need for a deserialization
workaround in the `value` method.
[[1]](diffhunk://#diff-b4bdde42c1ad0f584073818bd43dbd865b1b3b50d4701b131979f900d7c68297L22-R22)
[[2]](diffhunk://#diff-b4bdde42c1ad0f584073818bd43dbd865b1b3b50d4701b131979f900d7c68297L36-L39)
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
Adds a Platform API endpoint that allows migrating existing Google and
Microsoft email channels (with OAuth credentials) into Chatwoot without
requiring end-users to re-authenticate. This enables customers who lack
Rails console access to programmatically migrate email channels from
legacy systems.
### How to test
1. Create a Platform App and grant it permissible access to a target
account
2. `POST /platform/api/v1/accounts/:account_id/email_channel_migrations`
with a payload like:
```json
{
"migrations": [
{
"email": "support@example.com",
"provider": "google",
"provider_config": {
"access_token": "...",
"refresh_token": "...",
"expires_on": "..."
},
"inbox_name": "Migrated Support"
}
]
}
```
3. Verify channels are created with correct provider, provider_config, and IMAP defaults
4. Verify partial failures (e.g. duplicate email) don't roll back other migrations in the batch
5. Verify unauthenticated and non-permissible requests return 401
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
Webhook payloads (`message_created`, `message_updated`) started sending
channel-rendered HTML in the `content` field instead of the original raw
message content after PR #12878. This broke downstream agent bots and
integrations that expected plain text or markdown.
Closes https://linear.app/chatwoot/issue/PLA-109/webhook-payloads-send-channel-rendered-html-instead-of-raw-content
## How to reproduce
1. Connect an agent bot to a WebWidget inbox
2. Send a message with markdown formatting (e.g. `**bold**`) from the
widget
3. Observe the agent bot webhook payload — `content` field contains
`<p><strong>bold</strong></p>` instead of `**bold**`
## What changed
Split `MessageContentPresenter` into two public methods:
- `outgoing_content` — renders markdown for the target channel (used by
channel delivery services)
- `webhook_content` — returns raw content with CSAT survey URL when
applicable, no markdown rendering (used by `webhook_data`)
Updated `Message#webhook_data` to use `webhook_content` instead of
`outgoing_content`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
## Summary
- Adds `LimitNOFILE=65536` to both web and worker systemd service units
- Fixes recurring `Errno::EMFILE` (Too many open files) errors during
peak traffic after deploys
## Context
Puma workers idle at 720-770 open FDs against the default soft limit of
1024, leaving ~250 FDs of headroom. During deploy-triggered instance
refreshes at peak traffic, concurrent requests exhaust the remaining
FDs, causing EMFILE across all web instances.
3 incidents in March 2026 with escalating event counts. The hard limit
is already 524288, so this just raises the soft limit to a standard
production value.
Self-hosted instances pick this up automatically via `cwctl --upgrade`.
Fixes
https://linear.app/chatwoot/issue/CW-6685/errnoemfile-too-many-open-files-rb-sysopen
# Pull Request Template
## Description
Relocate controller from enterprise/ to app/ and add
Api::V1::Accounts::Captain::TasksController.prepend_mod_with for EE
overrides.
Fixes: Ai assist giving 404 on CE
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
before:
<img width="482" height="130" alt="image"
src="https://github.com/user-attachments/assets/f51dc28a-ac54-45c4-9015-6f956fdf5057"
/>
after:
<img width="458" height="182" alt="image"
src="https://github.com/user-attachments/assets/eb86a679-5482-4157-9f4e-f3e9953d8649"
/>
## 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
- [x] 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 fixes the conversation list showing raw "**in less than a
minute**" text instead of "**now**" for very recent conversations.
Fixes https://linear.app/chatwoot/issue/CW-6666/issue-with-timestamps
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## 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
Webhook retry failure logs for agent-bot now include the event payload,
making it easier to identify which event failed when debugging transient
upstream errors (429/500).
Previously the log only showed:
`[AgentBots::WebhookJob] attempt 1 failed
RestClient::InternalServerError`
Now it includes the payload:
`[AgentBots::WebhookJob] attempt 1 failed
RestClient::InternalServerError payload={"event":"message_created",...}`
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
# Pull Request Template
## Description
- Skip handoff when the conversation is no longer pending.
- Fetch conversation status with Conversation.uncached to avoid stale
query cache when checking pending state.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
Difficult to reproduce locally, but seen a couple of conversations where
bot hands off an extra time or Captain re-generates usually due to a
retry on FaradayTimeoutError but at perform time, the conversation
status is stale so it appears as if Captain responded in an open
conversation.
## 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
- [x] Any dependent changes have been merged and published in downstream
modules
# Pull Request Template
## Description
For our account, the conversation completion evaluator was proving to be
too conservative. This resulted in queue noise.
This PR adds a line to handle gibberish/single worded messages with no
further context.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
<img width="3012" height="978" alt="CleanShot 2026-03-23 at 11 44 55@2x"
src="https://github.com/user-attachments/assets/328195e8-6ea0-4c3a-9049-ee80196eecad"
/>
<img width="2202" height="866" alt="CleanShot 2026-03-23 at 11 47 15@2x"
src="https://github.com/user-attachments/assets/8d51cff4-b18f-4582-9455-8119ec7eff3a"
/>
## 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
- [x] Any dependent changes have been merged and published in downstream
modules
# Pull Request Template
## Description
This PR adds support to auto-focus the editor when clicking reply to
this message, the editor now automatically receives focus so users can
start typing immediately.
Fixes
https://linear.app/chatwoot/issue/CW-6661/typing-box-not-focused-after-clicking-reply-to-message
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
### Screencast
https://github.com/user-attachments/assets/c5e77055-3f68-4ad8-934e-cfc465166e8a
## 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
## Description
The RemoveOrphanConversationsService filters orphan conversations by a
time window before deleting them. Previously it used created_at, which
could miss old conversations that still had recent activity.
Switching to last_activity_at ensures the cleanup window reflects actual
conversation activity rather than creation time.
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
- By running Rake task
- Run the job from console
## 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
# Pull Request Template
## Description
Captain v1 does not have access to contact attributes. Added a toggle to
let user choose if they want contact information available to Captain.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
Specs and locally
<img width="1924" height="740" alt="CleanShot 2026-03-19 at 18 48 19@2x"
src="https://github.com/user-attachments/assets/353cfeaa-cd58-40eb-89e7-d660a1dc1185"
/>
![Uploading CleanShot 2026-03-19 at 18.53.26@2x.png…]()
## 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
- [x] Any dependent changes have been merged and published in downstream
modules
## Description
Add missing JSON imports and spread exports for `companies`,
`contentTemplates`, `mfa`, `snooze`, `webhooks`, and `yearInReview` so
these translations are properly loaded in the pt_BR locale. Without
these imports, those sections of the UI were falling back to English for
Brazilian Portuguese users.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## 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
Co-authored-by: Sojan Jose <sojan@pepalo.com>
## PR#1: Reporting events rollup — model and write path
Reporting queries currently hit the `reporting_events` table directly.
This works, but the table grows linearly with event volume, and
aggregation queries (counts, averages over date ranges) get
progressively slower as accounts age.
This PR introduces a pre-aggregated `reporting_events_rollups` table
that stores daily per-metric, per-dimension (account/agent/inbox)
totals. The write path is intentionally decoupled from the read path —
rollup rows are written inline from the event listener via upsert, and a
backfill service exists to rebuild historical data from raw events.
Nothing reads from this table yet.
The write path activates when an account has a `reporting_timezone` set
(new account setting). The `reporting_events_rollup` feature flag
controls only the future read path, not writes — so rollup data
accumulates silently once timezone is configured. A `MetricRegistry`
maps raw event names to rollup column semantics in one place, keeping
the write and (future) read paths aligned.
### What changed
- Migration for `reporting_events_rollups` with a unique composite index
for upsert
- `ReportingEventsRollup` model
- `reporting_timezone` account setting with IANA timezone validation
- `MetricRegistry` — single source of truth for event-to-metric mappings
- `RollupService` — real-time upsert from event listener
- `BackfillService` — rebuilds rollups for a given account + date from
raw events
- Rake tasks for interactive backfill and timezone setup
- `reporting_events_rollup` feature flag (disabled by default)
### How to test
1. Set a `reporting_timezone` on an account
(`Account.first.update!(reporting_timezone: 'Asia/Kolkata')`)
2. Resolve a conversation or trigger a first response
3. Check `ReportingEventsRollup.where(account_id: ...)` — rows should
appear
4. Run backfill: `bundle exec rake reporting_events_rollup:backfill` and
verify historical data populates
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
## Description
This PR updates the OpenAPI schema definitions for Public API resources
(`public_conversation`, `public_message`, `public_contact`) so they
match the actual API responses produced by the jbuilder views.
These definitions were introduced in #2417 (2021-06) with a minimal set
of fields. The jbuilder views have since been updated (e.g. `uuid` in
#7255, `agent_last_seen_at` in #4377), but the Swagger definitions were
never updated. As a result, generated API clients get incorrect or
missing types. This change fixes that by aligning the schemas with the
implementation.
**Fixes #13692**
## 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)
- [x] This change requires a documentation update
## How Has This Been Tested?
- Compared each jbuilder view
(`app/views/public/api/v1/models/_conversation.json.jbuilder`,
`_message.json.jbuilder`, `_contact.json.jbuilder`) field-by-field
against the Swagger YAML definitions.
- Cross-referenced Ruby model enums (`Conversation.status`,
`Attachment.file_type`, `Message.message_type`) for enum values.
- Ran the swagger build (via the project’s `rake swagger:build` logic /
`json_refs` resolution) to regenerate `swagger.json` and tag group
files; confirmed the generated schemas contain the correct fields and
types.
- No runtime tests were run; this is a documentation/schema-only change.
## 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 *(N/A: schema definitions)*
- [x] 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 *(N/A: documentation/schema only)*
- [ ] New and existing unit tests pass locally with my changes *(N/A: no
code under test)*
- [x] Any dependent changes have been merged and published in downstream
modules *(N/A: none)*
---
## Change summary (for reference)
### `public_conversation`
- Added missing fields: `uuid`, `status`, `contact_last_seen_at`,
`agent_last_seen_at`
- Fixed `inbox_id` type from `string` to `integer`
- Fixed `messages` items `$ref` from `message` to `public_message`
- Added property details for embedded `contact` object (`id`, `name`,
`email`, `phone_number`)
- Added `status` enum: `open`, `resolved`, `pending`, `snoozed`
### `public_message`
- Fixed `id`, `message_type`, `created_at`, `conversation_id` types
(string → integer where applicable)
- Fixed `content_attributes` type from `string` to `object`
- Added property details for `attachments` items and `sender` object
- Added `file_type` and `sender.type` enums
### `public_contact`
- Added missing `phone_number` field
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
Devise 4.9.x has a race condition in the reconfirmable flow where
concurrent email change requests can desynchronize the confirmation
token from `unconfirmed_email`, letting an attacker confirm an email
they don't own. We use `:confirmable` with `reconfirmable = true`, so
we're directly exposed.
The upstream fix is in Devise 5.0.3, but we can't upgrade —
`devise-two-factor` only supports Devise 5 from v6.4.0, which also
raised its Rails minimum to 7.2+. No released version supports both
Devise 5 and Rails 7.1.
This PR ports the Devise 5.0.3 fix locally by overriding
`postpone_email_change_until_confirmation_and_regenerate_confirmation_token`
on the User model to persist the record before regenerating the token.
This is a stopgap — remove it once the dependency chain allows upgrading
to Devise 5.
### How to test
Sign in as a confirmed user and change your email. The app should send a
confirmation to the new address while keeping the current email
unchanged until confirmed.
The Platform API was returning the bot's `name` value for the
`outgoing_url` field across all agent bot endpoints (index, show,
create, update). A typo in the `_agent_bot.json.jbuilder` partial used
`resource.name` instead of `resource.outgoing_url`.
Closes#13787
## What changed
- `app/views/platform/api/v1/models/_agent_bot.json.jbuilder`: corrected
`resource.name` → `resource.outgoing_url` on the `outgoing_url` field.
## How to reproduce
1. Create an agent bot via `POST /platform/api/v1/agent_bots` with a
distinct `outgoing_url`.
2. Call `GET /platform/api/v1/agent_bots/:id`.
3. Before this fix the `outgoing_url` in the response equals the bot's
`name`; after the fix it equals the value set at creation.
## Tests
Added `outgoing_url` assertions to the existing GET index and GET show
request specs so the regression is covered.
# Pull Request Template
## Description
we were getting 403, 401 errors on `translate_query` on langfuse and
sentry
This happened because, we use the customer's openai key if they have
BYOK
But translation is something they never opt in so we should not use
their quota for it.
This PR addresses the issue.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration.
locally
## 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
- [x] Any dependent changes have been merged and published in downstream
modules
When `allowed_domains` is configured on a web widget inbox, the server
responds with Content-Security-Policy: frame-ancestors <domains>, which
blocks the widget iframe in mobile app WebViews. This happens because
WebViews load content from file:// or null origins, which cannot match
any domain in the frame-ancestors directive.
This adds a per-inbox toggle — "Enable widget in mobile apps" — that
skips the frame-ancestors header when the request has no valid Origin
(i.e., it comes from a mobile WebView). Web browsers with a real origin
still get domain restrictions enforced as usual.
<img width="2330" height="1490" alt="CleanShot 2026-03-11 at 10 13
01@2x"
src="https://github.com/user-attachments/assets/d9326fac-020d-4ce7-9ced-0c185468c8fc"
/>
Fixes
https://linear.app/chatwoot/issue/CW-6560/widget-is-not-loading-from-iosandroid-widgets
How to test
1. Go to Settings → Inboxes → (Web Widget) → Configuration
2. Set allowed_domains to a specific domain (e.g., *.example.com)
3. Try loading the widget in a mobile app WebView — it should be blocked
4. Enable "Enable widget in mobile apps" checkbox
5. Reload the widget in the WebView — it should now load successfully
6. Verify the widget on a website not in the allowed domains list is
still blocked
---------
Co-authored-by: iamsivin <iamsivin@gmail.com>
This change spreads Chatwoot Hub version checks across the day by
scheduling each installation at a stable minute derived from its
installation identifier, instead of having all instances check at the
same fixed time.
Closes
-
https://linear.app/chatwoot/issue/CW-6107/handle-the-spike-at-12-utc-on-chatwoot-hub
What changed
- Added `Internal::TriggerDailyScheduledItemsJob` to act as the daily
trigger for deferred internal jobs.
- Updated the version check cron entry to run once daily at `00:00 UTC`
and enqueue the actual version check for that installation’s assigned
minute of the day.
- Used a deterministic minute-of-day derived from
`ChatwootHub.installation_identifier` so the check time stays stable
across deploys and restarts.
- Kept the existing cron schedule key while switching it to the new
orchestrator job.
How to test
- Run `bundle exec rspec
spec/jobs/internal/check_new_versions_job_spec.rb
spec/jobs/internal/trigger_daily_scheduled_items_job_spec.rb
spec/configs/schedule_spec.rb`
- In a Rails console, run
`Internal::TriggerDailyScheduledItemsJob.perform_now` and verify
`Internal::CheckNewVersionsJob` is enqueued with a `wait_until` later
the same UTC day.
- In Super Admin settings, use Refresh and verify the version check
still runs immediately.
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
This adds a draft status for Help Center locales so teams can prepare
localized content in the dashboard without exposing those locales in the
public portal switcher until they are ready to publish.
Fixes: https://github.com/chatwoot/chatwoot/issues/10412
Closes: https://github.com/chatwoot/chatwoot/issues/10412
## Why
Teams need a way to work on locale-specific Help Center content ahead of
launch. The public portal should only show ready locales, while the
admin dashboard should continue to expose every allowed locale for
ongoing article and category work.
## What this change does
- Adds `draft_locales` to portal config as a subset of `allowed_locales`
- Hides drafted locales from the public portal language switchers while
keeping direct locale URLs working
- Keeps drafted locales fully visible in the admin dashboard for article
and category management
- Adds locale actions to move an existing locale to draft, publish a
drafted locale, and keep the default locale protected from drafting
- Adds a status dropdown when creating a locale so new locales can be
created as `Published` or `Draft`
- Returns each admin locale with a `draft` flag so the locale UI can
reflect the public visibility state
## Validation
- Seed a portal with multiple locales, draft one locale, and confirm the
public portal switcher hides it while `/hc/:slug/:locale` still loads
directly
- In the admin dashboard, confirm drafted locales still appear in the
locale list and remain selectable for articles and categories
- Create a new locale with `Draft` status and confirm it stays out of
the public switcher until published
- Move an existing locale back and forth between `Published` and `Draft`
and confirm the public switcher updates accordingly
## Demo
https://github.com/user-attachments/assets/ba22dc26-c2e7-463a-b1f5-adf1fda1f9be
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Fixes help center public article search so query responses stay compact
and locale-scoped. Whitespace-only queries are now treated as empty in
both the portal UI and the server-side search path, and search
suggestions stay aligned with the trimmed query.
Fixes: https://github.com/chatwoot/chatwoot/issues/10402
Closes: https://github.com/chatwoot/chatwoot/issues/10402
## Why
The public help center search endpoint reused the full article
serializer for query responses, which returned much more data than the
search suggestions UI needed. That made responses heavier than necessary
and also surfaced nested portal and category data that made the results
look cross-locale.
Whitespace-only searches could also still reach the backend search path,
and in Enterprise that meant embedding search could be invoked for a
blank query.
## What changed
- return a compact search-specific payload for article query responses
- keep the existing full article serializer for normal article listing
responses
- preserve current-locale search behavior for the portal search flow
- trim whitespace-only search terms on the client so they do not open
suggestions or trigger a request
- reuse the normalized query on the backend so whitespace-only requests
are treated as empty searches in both OSS and Enterprise paths
- pass the trimmed search term into suggestions so highlighting matches
the actual query being sent
- add request and frontend regression coverage for compact payloads,
locale scoping, and whitespace-only search behavior
## Validation
1. Open `/hc/:portal/:locale` in the public help center.
2. Enter only spaces in the search box and confirm suggestions do not
open.
3. Search for a real term and confirm suggestions appear.
4. Verify the results are limited to the active locale.
5. Click a suggestion and confirm it opens the correct article page.
6. Inspect the query response and confirm it returns the compact search
payload instead of the full article serializer.
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Email inbox replies now work for Google and Microsoft OAuth inboxes even
when the self-hosted instance does not have global SMTP configured. This
keeps agent replies working for email channels that already have valid
inbox-level delivery settings.
fixes: chatwoot/chatwoot#13118closes: chatwoot/chatwoot#13118
## Why
Self-hosted email inbox replies were blocked by a global SMTP guard in
the `email_reply` path. For OAuth-backed email inboxes, outbound
delivery is configured at the inbox level, so the mailer returned early
and the reply flow failed before sending.
## What this change does
- Allows the `email_reply` path to proceed when the inbox has SMTP
configured
- Allows the `email_reply` path to proceed when the inbox has Google or
Microsoft OAuth delivery configured
- Renames the touched mailer helper predicates to `?` methods for
clarity
## Validation
- Configure a Google email inbox on a self-hosted instance without
global `SMTP_ADDRESS`
- Reply from Chatwoot to an existing email conversation
- Confirm the reply is sent through the inbox OAuth SMTP configuration
- Run `bundle exec rspec
spec/mailers/conversation_reply_mailer_spec.rb:595`
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
https://linear.app/chatwoot/issue/CW-6595/vanta-remediate-high-vulnerabilities-identified-in-packages-are
## Description
- Added "rollup": ">=4.59.0" to pnpm.overrides in package.json
- This bumps rollup from 4.52.5 to 4.59.0 (the transitive dep via vite)
## Type of change
- [ ] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
- Overall Sanity via UI.
## 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: Muhsin Keloth <muhsinkeramam@gmail.com>
## Description
This PR improves the Ukrainian translation for the Chatwoot widget
(`app/javascript/widget/i18n/locale/uk.json`).
Key changes:
- Fixed typo: `Звантажити` → `Завантажити`
- Translated missing English strings
- Improved reply time messages
- Updated day names to match `{day}` usage in `BACK_ON_DAY`
- Improved UX wording in form placeholders
- Fixed typography in `ім’я`
- Improved consistency with other Chatwoot translations
These updates improve readability and correctness of the Ukrainian
widget interface.
## Type of change
- [x] Bug fix (non-breaking change which fixes an issue)
## How Has This Been Tested?
Reviewed the updated translations and verified that:
- Ukrainian translations render correctly
- Reply time messages display properly
- `{day}` values work correctly with the `BACK_ON_DAY` message
- Form placeholders appear correctly
- No untranslated English strings remain
## 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
---------
Co-authored-by: Sojan Jose <sojan@pepalo.com>
RubyLLM uses a static bundled models.json as its model registry. New
models released by providers (e.g., gpt-5.1, gpt-5.2) are not available
until the gem is updated, causing ModelNotFoundError for self-hosted
users who configure newer models.
Refresh the model registry synchronously during Llm::Config.initialize!
to fetch the latest model list from configured providers and models.dev,
then persist with save_to_json. This runs once per process on the first
LLM request, adding ~1-2s to that request. Falls back gracefully to the
bundled registry on failure.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
RubyLLM uses a static bundled models.json as its model registry. New
models released by providers (e.g., gpt-5.1, gpt-5.2) are not available
until the gem is updated, causing ModelNotFoundError for self-hosted
users who configure newer models.
Refresh the model registry asynchronously via a background thread during
Llm::Config.initialize! to fetch the latest model list from configured
providers and models.dev, then persist with save_to_json. The bundled
registry is available immediately; the refresh updates it in the
background without blocking the first LLM request.
Use double-checked locking with a mutex to prevent concurrent threads
from spawning duplicate refresh operations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
RubyLLM uses a static bundled models.json file as its model registry.
New models released by providers (e.g., gpt-5.1, gpt-5.2) are not
available until the gem is updated. This causes ModelNotFoundError
for self-hosted users who configure newer models.
Call RubyLLM.models.refresh! during Llm::Config.initialize! to fetch
the latest model list from configured providers and models.dev. Persist
the result with save_to_json to tmp/ruby_llm_models.json so subsequent
processes load from disk instead of making HTTP calls. The file has a
1-hour TTL to balance freshness with performance.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 21:38:30 +05:30
777 changed files with 64905 additions and 45845 deletions
# You may wonder why we're not handling the refresh token update, since the token will expire only after 10 years, https://github.com/linear/linear/issues/251
"DATA_FETCHING_FAILED":"Failed to fetch data, please try again later.",
"SUMMARY_FETCHING_FAILED":"Failed to fetch summary, please try again later.",
"METRICS":{
"CONVERSATIONS":{
"NAME":"Conversations",
"DESC":"( Total )"
"NAME":"ውይይቶች",
"DESC":"( ጠቅላላ )"
},
"INCOMING_MESSAGES":{
"NAME":"Messages received",
"DESC":"( Total )"
"DESC":"( ጠቅላላ )"
},
"OUTGOING_MESSAGES":{
"NAME":"Messages sent",
"DESC":"( Total )"
"DESC":"( ጠቅላላ )"
},
"FIRST_RESPONSE_TIME":{
"NAME":"First Response Time",
"DESC":"( Avg )",
"DESC":"( አማካይ )",
"INFO_TEXT":"Total number of conversations used for computation:",
"TOOLTIP_TEXT":"First Response Time is {metricValue} (based on {conversationCount} conversations)"
},
"RESOLUTION_TIME":{
"NAME":"Resolution Time",
"DESC":"( Avg )",
"NAME":"የመፍትሄ ጊዜ",
"DESC":"( አማካይ )",
"INFO_TEXT":"Total number of conversations used for computation:",
"TOOLTIP_TEXT":"Resolution Time is {metricValue} (based on {conversationCount} conversations)"
},
"RESOLUTION_COUNT":{
"NAME":"Resolution Count",
"DESC":"( Total )"
"NAME":"የመፍትሄ ብዛት",
"DESC":"( ጠቅላላ )"
},
"BOT_RESOLUTION_COUNT":{
"NAME":"Resolution Count",
@@ -61,8 +61,8 @@
"CUSTOM_DATE_RANGE":"Custom date range"
},
"CUSTOM_DATE_RANGE":{
"CONFIRM":"Apply",
"PLACEHOLDER":"Select date range"
"CONFIRM":"አተግባር ላይ አውርድ",
"PLACEHOLDER":"የቀን ክልል ይምረጡ"
},
"GROUP_BY_FILTER_DROPDOWN_LABEL":"Group By",
"DURATION_FILTER_LABEL":"Duration",
@@ -127,12 +127,12 @@
}
},
"AGENT_REPORTS":{
"HEADER":"Agents Overview",
"HEADER":"የወኪሎች እይታ",
"DESCRIPTION":"Easily track agent performance with key metrics such as conversations, response times, resolution times, and resolved cases. Click an agent's name to learn more.",
"LOADING_CHART":"Loading chart data...",
"NO_ENOUGH_DATA":"We've not received enough data points to generate report, Please try again later.",
"INFO_TEXT":"Total number of conversations used for computation:",
"TOOLTIP_TEXT":"First Response Time is {metricValue} (based on {conversationCount} conversations)"
},
"RESOLUTION_TIME":{
"NAME":"Resolution Time",
"DESC":"( Avg )",
"NAME":"የመፍትሄ ጊዜ",
"DESC":"( አማካይ )",
"INFO_TEXT":"Total number of conversations used for computation:",
"TOOLTIP_TEXT":"Resolution Time is {metricValue} (based on {conversationCount} conversations)"
},
"RESOLUTION_COUNT":{
"NAME":"Resolution Count",
"DESC":"( Total )"
"NAME":"የተፈታ ብዛት",
"DESC":"( ጠቅላላ )"
}
},
"DATE_RANGE":[
{
"id":0,
"name":"Last 7 days"
"name":"ያለፉት 7 ቀናት"
},
{
"id":1,
"name":"Last 30 days"
"name":"ያለፉት 30 ቀናት"
},
{
"id":2,
"name":"Last 3 months"
"name":"ያለፉት 3 ወራት"
},
{
"id":3,
"name":"Last 6 months"
"name":"ያለፉት 6 ወራት"
},
{
"id":4,
"name":"Last year"
"name":"ያለፈው ዓመት"
},
{
"id":5,
"name":"Custom date range"
"name":"በተለይ የተመረጠ ቀን ክልል"
}
],
"CUSTOM_DATE_RANGE":{
"CONFIRM":"Apply",
"PLACEHOLDER":"Select date range"
"CONFIRM":"ተግባሩን ተፈጽም",
"PLACEHOLDER":"የቀን ክልል ይምረጡ"
}
},
"LABEL_REPORTS":{
"HEADER":"Labels Overview",
"HEADER":"የመለያ አጠቃላይ እይታ",
"DESCRIPTION":"Track label performance with key metrics including conversations, response times, resolution times, and resolved cases. Click a label name for detailed insights.",
"LOADING_CHART":"Loading chart data...",
"NO_ENOUGH_DATA":"We've not received enough data points to generate report, Please try again later.",
"INFO_TEXT":"Total number of conversations used for computation:",
"TOOLTIP_TEXT":"First Response Time is {metricValue} (based on {conversationCount} conversations)"
},
"RESOLUTION_TIME":{
"NAME":"Resolution Time",
"DESC":"( Avg )",
"NAME":"የመፍትሄ ጊዜ",
"DESC":"( አማካይ )",
"INFO_TEXT":"Total number of conversations used for computation:",
"TOOLTIP_TEXT":"Resolution Time is {metricValue} (based on {conversationCount} conversations)"
},
"RESOLUTION_COUNT":{
"NAME":"Resolution Count",
"DESC":"( Total )"
"NAME":"የመፍትሄ ብዛት",
"DESC":"( ድምር )"
}
},
"DATE_RANGE":[
{
"id":0,
"name":"Last 7 days"
"name":"ያለፉ 7 ቀናት"
},
{
"id":1,
"name":"Last 30 days"
"name":"ያለፉ 30 ቀናት"
},
{
"id":2,
"name":"Last 3 months"
"name":"ያለፉት 3 ወራት"
},
{
"id":3,
"name":"Last 6 months"
"name":"ያለፉት 6 ወራት"
},
{
"id":4,
"name":"Last year"
"name":"ያለፈው ዓመት"
},
{
"id":5,
"name":"Custom date range"
"name":"በተፈጥሮ የተመረጠ የቀን ክልል"
}
],
"CUSTOM_DATE_RANGE":{
"CONFIRM":"Apply",
"PLACEHOLDER":"Select date range"
"CONFIRM":"ተግባር አድርግ",
"PLACEHOLDER":"የቀን ክልል ይምረጡ"
}
},
"INBOX_REPORTS":{
"HEADER":"Inbox Overview",
"HEADER":"የኢንቦክስ አጠቃላይ እይታ",
"DESCRIPTION":"Quickly view your inbox performance with key metrics like conversations, response times, resolution times, and resolved cases—all in one place. Click an inbox name for more details.",
"LOADING_CHART":"Loading chart data...",
"NO_ENOUGH_DATA":"We've not received enough data points to generate report, Please try again later.",
"MESSENGER_SUB_HEAD":"ضع هذا الكود داخل وسم الـ body في موقعك",
"ALLOWED_DOMAINS":{
"TITLE":"Allowed Domains",
"SUBTITLE":"Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER":"Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
"DESCRIPTION":"Restrict which websites can embed your chat widget. For security, only add domains you own and trust. Add one or more domains separated by commas. Leave blank to allow all domains (not recommended for production).",
"SUBTITLE":"حدد هذا الخيار إذا كنت تقوم بتضمين الأداة في تطبيقات iOS أو Android. لا ترسل تطبيقات الجوال معلومات النطاق، لذا سيتم حظرها بسبب قيود النطاق ما لم يتم تفعيل هذا الخيار."
},
"IDENTITY_VALIDATION":{
"TITLE":"Identity Validation",
"DESCRIPTION":"Verify user authenticity by generating secure tokens. This prevents unauthorized users from impersonating others in your chat.",
"SECRET_KEY":"Secret Key",
"VIEW_DOCS":"View documentation",
"REQUIRE_LABEL":"Require identity validation for all conversations",
"REQUIRE_DESCRIPTION":"When enabled, users must provide a valid identity token to start conversations. Requests without valid tokens will be rejected."
},
"INBOX_AGENTS":"وكيل الدعم",
"INBOX_AGENTS_SUB_TEXT":"إضافة أو إزالة وكلاء من صندوق الوارد هذا",
"BODY":"With this integration, all of your incoming conversations will be synced to the ***{selectedChannelName}*** channel in your Slack workspace. You can manage all your customer conversations right within the channel and never miss a message.\n\nHere are the main features of the integration:\n\n**Respond to conversations from within Slack:** To respond to a conversation in the ***{selectedChannelName}*** Slack channel, simply type out your message and send it as a thread. This will create a response back to the customer through Chatwoot. It's that simple!\n\n **Create private notes:** If you want to create private notes instead of replies, start your message with ***`note:`***. This ensures that your message is kept private and won't be visible to the customer.\n\n**Associate an agent profile:** If the person who replied on Slack has an agent profile in Chatwoot under the same email, the replies will be associated with that agent profile automatically. This means you can easily track who said what and when. On the other hand, when the replier doesn't have an associated agent profile, the replies will appear from the bot profile to the customer.",
"BODY":"باستخدام هذا التكامل، ستتم مزامنة جميع محادثاتك الواردة مع قناة ***{selectedChannelName}*** في مساحة عمل Slack الخاصة بك. يمكنك إدارة جميع محادثات عملائك مباشرة من داخل القناة ولن تفوّت أي رسالة.\n\nفيما يلي الميزات الرئيسية لهذا التكامل:\n\n**الرد على المحادثات من داخل Slack:** للرد على محادثة في قناة Slack ***{selectedChannelName}***، ما عليك سوى كتابة رسالتك وإرسالها كسلسلة رسائل. سيؤدي ذلك إلى إنشاء رد للعميل عبر Chatwoot. الأمر بهذه البساطة!\n\n **إنشاء ملاحظات خاصة:** إذا كنت تريد إنشاء ملاحظات خاصة بدلاً من الردود، فابدأ رسالتك بـ ***`note:`***. يضمن ذلك بقاء رسالتك خاصة وعدم ظهورها للعميل.\n\n**ربط ملف وكيل:** إذا كان الشخص الذي ردّ في Slack لديه ملف وكيل في Chatwoot تحت البريد الإلكتروني نفسه، فسيتم ربط الردود تلقائيًا بذلك الملف. وهذا يعني أنه يمكنك بسهولة تتبع من قال ماذا ومتى. من ناحية أخرى، إذا لم يكن لدى الشخص الذي ردّ ملف وكيل مرتبط، فستظهر الردود للعميل على أنها صادرة من ملف البوت.",
"SELECTED":"selected"
},
"SELECT_CHANNEL":{
@@ -390,72 +390,72 @@
},
"CAPTAIN":{
"NAME":"قائد",
"HEADER_KNOW_MORE":"Know more",
"HEADER_KNOW_MORE":"اعرف المزيد",
"ASSISTANT_SWITCHER":{
"ASSISTANTS":"Assistants",
"SWITCH_ASSISTANT":"Switch between assistants",
"NEW_ASSISTANT":"Create Assistant",
"EMPTY_LIST":"No assistants found, please create one to get started"
"ASSISTANTS":"المساعدون",
"SWITCH_ASSISTANT":"التبديل بين المساعدين",
"NEW_ASSISTANT":"إنشاء مساعد",
"EMPTY_LIST":"لم يتم العثور على مساعدين، يرجى إنشاء واحد للبدء"
},
"COPILOT":{
"TITLE":"Copilot",
"TRY_THESE_PROMPTS":"Try these prompts",
"PANEL_TITLE":"Get started with Copilot",
"KICK_OFF_MESSAGE":"Need a quick summary, want to check past conversations, or draft a better reply? Copilot’s here to speed things up.",
"PANEL_TITLE":"ابدأ مع Copilot",
"KICK_OFF_MESSAGE":"هل تحتاج إلى ملخص سريع، ترغب في مراجعة المحادثات السابقة، أو صياغة رد أفضل؟ Copilot هنا لتسريع الأمور.",
"SEND_MESSAGE":"إرسال الرسالة...",
"EMPTY_MESSAGE":"There was an error generating the response. Please try again.",
"LOADER":"Captain is thinking",
"EMPTY_MESSAGE":"حدث خطأ أثناء توليد الاستجابة. يرجى المحاولة مرة أخرى.",
"LOADER":"يقوم Captain بالتفكير",
"YOU":"أنت",
"USE":"Use this",
"RESET":"Reset",
"SHOW_STEPS":"Show steps",
"SELECT_ASSISTANT":"Select Assistant",
"USE":"استخدام هذا",
"RESET":"إعادة تعيين",
"SHOW_STEPS":"عرض الخطوات",
"SELECT_ASSISTANT":"اختر المساعد",
"PROMPTS":{
"SUMMARIZE":{
"LABEL":"Summarize this conversation",
"CONTENT":"Summarize the key points discussed between the customer and the support agent, including the customer's concerns, questions, and the solutions or responses provided by the support agent"
"LABEL":"لخص هذه المحادثة",
"CONTENT":"لخص النقاط الرئيسية التي نوقشت بين العميل ووكيل الدعم، بما في ذلك مخاوف العميل، أسئلته، والحلول أو الردود المقدمة من الوكيل."
},
"SUGGEST":{
"LABEL":"Suggest an answer",
"CONTENT":"Analyze the customer's inquiry, and draft a response that effectively addresses their concerns or questions. Ensure the reply is clear, concise, and provides helpful information."
"LABEL":"اقترح إجابة",
"CONTENT":"حلل استفسار العميل وقم بصياغة رد يعالج مخاوفه أو أسئلته بفعالية. تأكد من أن الرد واضح، موجز، ويوفر معلومات مفيدة."
},
"RATE":{
"LABEL":"Rate this conversation",
"CONTENT":"Review the conversation to see how well it meets the customer's needs. Share a rating out of 5 based on tone, clarity, and effectiveness."
"LABEL":"قم بتقييم هذه المحادثة",
"CONTENT":"راجع المحادثة لترى مدى تلبيتها لاحتياجات العميل. شارك تقييمًا من 5 بناءً على النغمة، الوضوح، والفعالية."
},
"HIGH_PRIORITY":{
"LABEL":"High priority conversations",
"CONTENT":"Give me a summary of all high priority open conversations. Include the conversation ID, customer name (if available), last message content, and assigned agent. Group by status if relevant."
"LABEL":"المحادثات ذات الأولوية العالية",
"CONTENT":"اعطني ملخصًا لجميع المحادثات المفتوحة ذات الأولوية العالية. تضمّن معرف المحادثة، اسم العميل (إن وُجد)، محتوى آخر رسالة، والوكيل المعين. قم بالتجميع حسب الحالة إذا كان ذلك مناسبًا."
},
"LIST_CONTACTS":{
"LABEL":"List contacts",
"CONTENT":"Show me the list of top 10 contacts. Include name, email or phone number (if available), last seen time, tags (if any)."
"LABEL":"قائمة جهات الاتصال",
"CONTENT":"اعرض لي قائمة بأفضل 10 جهات اتصال. تضمّن الاسم، البريد الإلكتروني أو رقم الهاتف (إن وُجد)، آخر وقت مشاهدة، العلامات (إن وجدت)."
}
}
},
"PLAYGROUND":{
"USER":"أنت",
"ASSISTANT":"Assistant",
"ASSISTANT":"مساعد",
"MESSAGE_PLACEHOLDER":"أكتب رسالتك...",
"HEADER":"Playground",
"DESCRIPTION":"Use this playground to send messages to your assistant and check if it responds accurately, quickly, and in the tone you expect.",
"CREDIT_NOTE":"Messages sent here will count toward your Captain credits."
"HEADER":"ساحة اللعب",
"DESCRIPTION":"استخدم هذه الساحة لإرسال رسائل إلى مساعدك والتحقق مما إذا كان يرد بدقة وسرعة وبالنغمة التي تتوقعها.",
"CREDIT_NOTE":"الرسائل المرسلة هنا ستُحتسب ضمن رصيد Captain الخاص بك."
},
"PAYWALL":{
"TITLE":"Upgrade to use Captain AI",
"AVAILABLE_ON":"Captain is not available on the free plan.",
"UPGRADE_PROMPT":"Upgrade your plan to get access to our assistants, copilot and more.",
"TITLE":"قم بالترقية لاستخدام Captain AI",
"AVAILABLE_ON":"Captain غير متاح على الخطة المجانية.",
"UPGRADE_PROMPT":"قم بترقية خطتك للحصول على الوصول إلى مساعدينا، وcopilot، والمزيد.",
"UPGRADE_NOW":"Upgrade now",
"CANCEL_ANYTIME":"You can change or cancel your plan anytime"
},
"ENTERPRISE_PAYWALL":{
"AVAILABLE_ON":"ولا يتوفر الكابتن AI إلا في خطط المؤسسة.",
"UPGRADE_PROMPT":"Upgrade your plan to get access to our assistants, copilot and more.",
"UPGRADE_PROMPT":"قم بترقية خطتك للحصول على الوصول إلى مساعدينا، وcopilot، والمزيد.",
"ASK_ADMIN":"Please reach out to your administrator for the upgrade."
},
"BANNER":{
"RESPONSES":"You've used over 80% of your response limit. To continue using Captain AI, please upgrade.",
"DOCUMENTS":"Document limit reached. Upgrade to continue using Captain AI."
"RESPONSES":"لقد استخدمت أكثر من 80٪ من حد الاستجابات الخاص بك. للاستمرار في استخدام Captain AI، يرجى الترقية.",
"DOCUMENTS":"تم الوصول إلى حد المستندات. قم بالترقية للاستمرار في استخدام Captain AI."
},
"FORM":{
"CANCEL":"إلغاء",
@@ -527,7 +527,8 @@
"TITLE":"الخصائص",
"ALLOW_CONVERSATION_FAQS":"Generate FAQs from resolved conversations",
"ALLOW_MEMORIES":"Capture key details as memories from customer interactions.",
"ALLOW_CITATIONS":"Include source citations in responses"
"ALLOW_CITATIONS":"Include source citations in responses",
"ALLOW_CONTACT_ATTRIBUTES":"Allow access to contact information"
}
},
"EDIT":{
@@ -737,6 +738,17 @@
"DOCUMENTS":{
"HEADER":"Documents",
"ADD_NEW":"Create a new document",
"SELECTED":"{count} selected",
"SELECT_ALL":"Select all ({count})",
"UNSELECT_ALL":"Unselect all ({count})",
"BULK_DELETE_BUTTON":"حذف",
"BULK_DELETE":{
"TITLE":"Delete documents?",
"DESCRIPTION":"Are you sure you want to delete the selected documents? This action cannot be undone.",
"ERROR_MESSAGE":"There was an error deleting the documents, please try again."
},
"RELATED_RESPONSES":{
"TITLE":"Related FAQs",
"DESCRIPTION":"These FAQs are generated directly from the document."
@@ -794,6 +806,7 @@
"CUSTOM_TOOLS":{
"HEADER":"Tools",
"ADD_NEW":"Create a new tool",
"SOFT_LIMIT_WARNING":"Having more than 10 tools may reduce the assistant's reliability in selecting the right tool. Consider removing unused tools for better results.",
"EMPTY_STATE":{
"TITLE":"No custom tools available",
"SUBTITLE":"Create custom tools to connect your assistant with external APIs and services, enabling it to fetch data and perform actions on your behalf.",
"ERROR_MESSAGE":"Could not delete contact. Please try again later."
"ERROR_MESSAGE":"Əlaqəni silmək mümkün olmadı. Zəhmət olmasa, bir az sonra yenidən cəhd edin."
}
},
"CONTACT_FORM":{
"FORM":{
"SUBMIT":"Submit",
"CANCEL":"Cancel",
"CANCEL":"Ləğv et",
"AVATAR":{
"LABEL":"Contact Avatar"
},
"NAME":{
"PLACEHOLDER":"Enter the full name of the contact",
"LABEL":"Full Name"
"PLACEHOLDER":"Əlaqənin tam adını daxil edin",
"LABEL":"Tam Ad"
},
"BIO":{
"PLACEHOLDER":"Enter the bio of the contact",
@@ -100,30 +100,30 @@
"ERROR":"Please enter a valid email address."
},
"PHONE_NUMBER":{
"PLACEHOLDER":"Enter the phone number of the contact",
"LABEL":"Phone Number",
"HELP":"Phone number should be of E.164 format eg: +1415555555 [+][country code][area code][local phone number]. You can select the dial code from the dropdown.",
"ERROR":"Phone number should be either empty or of E.164 format",
"DIAL_CODE_ERROR":"Please select a dial code from the list",
"DUPLICATE":"This phone number is in use for another contact."
"PLACEHOLDER":"Əlaqə şəxsin telefon nömrəsini daxil edin",
"ERROR":"Telefon nömrəsi ya boş olmalıdır, ya da E.164 formatında olmalıdır",
"DIAL_CODE_ERROR":"Zəhmət olmasa siyahıdan kodu seçin",
"DUPLICATE":"Bu telefon nömrəsi başqa bir əlaqə üçün istifadə olunur."
},
"LOCATION":{
"PLACEHOLDER":"Enter the location of the contact",
"LABEL":"Location"
"PLACEHOLDER":"Əlaqə yerini daxil edin",
"LABEL":"Yer"
},
"COMPANY_NAME":{
"PLACEHOLDER":"Enter the company name",
"PLACEHOLDER":"Şirkət adını daxil edin",
"LABEL":"Company Name"
},
"COUNTRY":{
"PLACEHOLDER":"Enter the country name",
"PLACEHOLDER":"Ölkə adını daxil edin",
"LABEL":"Country Name",
"SELECT_PLACEHOLDER":"Select",
"REMOVE":"Remove",
"SELECT_COUNTRY":"Select Country"
"SELECT_PLACEHOLDER":"Seçin",
"REMOVE":"Sil",
"SELECT_COUNTRY":"Ölkəni seçin"
},
"CITY":{
"PLACEHOLDER":"Enter the city name",
"PLACEHOLDER":"Şəhərin adını daxil edin",
"LABEL":"City Name"
},
"SOCIAL_PROFILES":{
@@ -155,23 +155,23 @@
"ERROR_MESSAGE":"There was an error, please try again"
},
"NEW_CONVERSATION":{
"BUTTON_LABEL":"Start conversation",
"TITLE":"New conversation",
"BUTTON_LABEL":"Söhbətə başla",
"TITLE":"Yeni söhbət",
"DESC":"Start a new conversation by sending a new message.",
"NO_INBOX":"Couldn't find an inbox to initiate a new conversation with this contact.",
"FORM":{
"TO":{
"LABEL":"To"
"LABEL":"Kimə"
},
"INBOX":{
"LABEL":"Via Inbox",
"PLACEHOLDER":"Choose source inbox",
"ERROR":"Select an inbox"
"ERROR":"Bir qutu seçin"
},
"SUBJECT":{
"LABEL":"Subject",
"PLACEHOLDER":"Subject",
"ERROR":"Subject can't be empty"
"LABEL":"Mövzu",
"PLACEHOLDER":"Mövzu",
"ERROR":"Mövzu boş ola bilməz"
},
"MESSAGE":{
"LABEL":"Message",
@@ -183,10 +183,10 @@
"HELP_TEXT":"Drag and drop files here or choose files to attach"
},
"SUBMIT":"Send message",
"CANCEL":"Cancel",
"CANCEL":"Ləğv et",
"SUCCESS_MESSAGE":"Message sent!",
"GO_TO_CONVERSATION":"View",
"ERROR_MESSAGE":"Couldn't send! try again"
"GO_TO_CONVERSATION":"Bax",
"ERROR_MESSAGE":"Göndərmək mümkün olmadı! yenidən cəhd et"
}
},
"CONTACTS_PAGE":{
@@ -204,7 +204,7 @@
"ACTIONS":{
"COPY":"Copy attribute",
"DELETE":"Delete attribute",
"EDIT":"Edit attribute"
"EDIT":"Xüsusiyyəti redaktə et"
},
"ADD":{
"TITLE":"Create custom attribute",
@@ -212,7 +212,7 @@
},
"FORM":{
"CREATE":"Add attribute",
"CANCEL":"Cancel",
"CANCEL":"Ləğv et",
"NAME":{
"LABEL":"Custom attribute name",
"PLACEHOLDER":"Eg: shopify id",
@@ -220,12 +220,12 @@
},
"VALUE":{
"LABEL":"Attribute value",
"PLACEHOLDER":"Eg: 11901 "
"PLACEHOLDER":"Məsələn: 11901 "
},
"ADD":{
"TITLE":"Create new attribute ",
"SUCCESS":"Attribute added successfully",
"ERROR":"Unable to add attribute. Please try again later"
"SUCCESS":"Xüsusiyyət uğurla əlavə edildi",
"ERROR":"Xüsusiyyəti əlavə etmək mümkün olmadı. Zəhmət olmasa, bir az sonra yenidən cəhd edin"
},
"UPDATE":{
"SUCCESS":"Attribute updated successfully",
@@ -236,52 +236,52 @@
"ERROR":"Unable to delete attribute. Please try again later"
},
"ATTRIBUTE_SELECT":{
"TITLE":"Add attributes",
"TITLE":"Xüsusiyyətlər əlavə et",
"PLACEHOLDER":"Search attributes",
"NO_RESULT":"No attributes found"
},
"ATTRIBUTE_TYPE":{
"LIST":{
"PLACEHOLDER":"Select value",
"PLACEHOLDER":"Dəyəri seçin",
"SEARCH_INPUT_PLACEHOLDER":"Search value",
"NO_RESULT":"No result found"
}
}
},
"VALIDATIONS":{
"REQUIRED":"Valid value is required",
"INVALID_URL":"Invalid URL",
"INVALID_INPUT":"Invalid Input"
"REQUIRED":"Düzgün dəyər tələb olunur",
"INVALID_URL":"Yanlış URL",
"INVALID_INPUT":"Yanlış Giriş"
}
},
"MERGE_CONTACTS":{
"TITLE":"Merge contacts",
"DESCRIPTION":"Merge contacts to combine two profiles into one, including all attributes and conversations. In case of conflict, the Primary contact’s attributes will take precedence.",
"DESCRIPTION":"İki profili bütün atributlar və söhbətlər daxil olmaqla birləşdirərək əlaqələri birləşdirin. Ziddiyyət olduqda, Əsas əlaqənin atributları üstünlük təşkil edəcək.",
"PRIMARY":{
"TITLE":"Primary contact",
"TITLE":"Əsas əlaqə",
"HELP_LABEL":"To be deleted"
},
"PARENT":{
"TITLE":"Contact to merge",
"PLACEHOLDER":"Search for a contact",
"PLACEHOLDER":"Əlaqə axtar",
"HELP_LABEL":"To be kept"
},
"SUMMARY":{
"TITLE":"Summary",
"TITLE":"Yekun",
"DELETE_WARNING":"Contact of <strong>{primaryContactName}</strong> will be deleted.",
"ATTRIBUTE_WARNING":"Contact details of <strong>{primaryContactName}</strong> will be copied to <strong>{parentContactName}</strong>."
},
"SEARCH":{
"ERROR_MESSAGE":"Something went wrong. Please try again later."
"ERROR_MESSAGE":"Nəsə səhv getdi. Zəhmət olmasa, bir az sonra yenidən cəhd edin."
},
"FORM":{
"SUBMIT":" Merge contacts",
"CANCEL":"Cancel",
"CANCEL":"Ləğv et",
"CHILD_CONTACT":{
"ERROR":"Select a child contact to merge"
"ERROR":"Birləşdirmək üçün alt əlaqəni seçin"
},
"SUCCESS_MESSAGE":"Contact merged successfully",
"ERROR_MESSAGE":"Could not merge contacts, try again!"
"ERROR_MESSAGE":"Əlaqələri birləşdirmək mümkün olmadı, yenidən cəhd edin!"
},
"DROPDOWN_ITEM":{
"ID":"(ID: {identifier})"
@@ -289,68 +289,68 @@
},
"CONTACTS_LAYOUT":{
"HEADER":{
"TITLE":"Contacts",
"SEARCH_TITLE":"Search contacts",
"ACTIVE_TITLE":"Active contacts",
"SEARCH_PLACEHOLDER":"Search...",
"TITLE":"Əlaqələr",
"SEARCH_TITLE":"Əlaqələrdə axtarış",
"ACTIVE_TITLE":"Aktiv əlaqələr",
"SEARCH_PLACEHOLDER":"Axtarış...",
"MESSAGE_BUTTON":"Message",
"SEND_MESSAGE":"Send message",
"BLOCK_CONTACT":"Block contact",
"UNBLOCK_CONTACT":"Unblock contact",
"BREADCRUMB":{
"CONTACTS":"Contacts"
"CONTACTS":"Əlaqələr"
},
"ACTIONS":{
"CONTACT_CREATION":{
"ADD_CONTACT":"Add contact",
"EXPORT_CONTACT":"Export contacts",
"IMPORT_CONTACT":"Import contacts",
"SAVE_CONTACT":"Save contact",
"ADD_CONTACT":"Əlaqə əlavə et",
"EXPORT_CONTACT":"Əlaqələri ixrac et",
"IMPORT_CONTACT":"Əlaqələri idxal et",
"SAVE_CONTACT":"Əlaqəni yadda saxla",
"EMAIL_ADDRESS_DUPLICATE":"This email address is in use for another contact.",
"PHONE_NUMBER_DUPLICATE":"This phone number is in use for another contact.",
"PHONE_NUMBER_DUPLICATE":"Bu telefon nömrəsi başqa bir əlaqə üçün istifadə olunur.",
"SUCCESS_MESSAGE":"Contact saved successfully",
"ERROR_MESSAGE":"Unable to save contact. Please try again later."
"ERROR_MESSAGE":"Əlaqəni saxlamaq mümkün olmadı. Zəhmət olmasa sonra yenidən cəhd edin."
},
"BLOCK_SUCCESS_MESSAGE":"This contact is blocked successfully",
"BLOCK_ERROR_MESSAGE":"Unable to block contact. Please try again later.",
"UNBLOCK_SUCCESS_MESSAGE":"This contact is unblocked successfully",
"UNBLOCK_SUCCESS_MESSAGE":"Bu əlaqənin bloku uğurla açıldı",
"UNBLOCK_ERROR_MESSAGE":"Unable to unblock contact. Please try again later.",
"IMPORT_CONTACT":{
"TITLE":"Import contacts",
"DESCRIPTION":"Import contacts through a CSV file.",
"TITLE":"Əlaqələri idxal et",
"DESCRIPTION":"Əlaqələri CSV faylı vasitəsilə idxal edin.",
"DOWNLOAD_LABEL":"Download a sample csv.",
"LABEL":"CSV File:",
"CHOOSE_FILE":"Choose file",
"CHANGE":"Change",
"CANCEL":"Cancel",
"IMPORT":"Import",
"SUCCESS_MESSAGE":"You will be notified via email when the import is complete.",
"LABEL":"CSV faylı:",
"CHOOSE_FILE":"Fayl seçin",
"CHANGE":"Dəyiş",
"CANCEL":"Ləğv et",
"IMPORT":"İdxal et",
"SUCCESS_MESSAGE":"İdxal bitdikdə sizə elektron bildiriş göndəriləcək.",
"ERROR_MESSAGE":"There was an error, please try again"
},
"EXPORT_CONTACT":{
"TITLE":"Export contacts",
"TITLE":"Əlaqələri ixrac et",
"DESCRIPTION":"Quickly export a csv file with comprehensive details of your contacts",
"CONFIRM":"Export",
"SUCCESS_MESSAGE":"Export is in progress. You will be notified on email when the export file is ready to download.",
"CONFIRM":"İxrac et",
"SUCCESS_MESSAGE":"İxrac davam edir. Fayl hazır olanda sizə elektron bildiriş göndəriləcək.",
"ERROR_MESSAGE":"There was an error, please try again"
},
"SORT_BY":{
"LABEL":"Sort by",
"LABEL":"Sırala",
"OPTIONS":{
"NAME":"Name",
"EMAIL":"Email",
"PHONE_NUMBER":"Phone number",
"COMPANY":"Company",
"COUNTRY":"Country",
"CITY":"City",
"LAST_ACTIVITY":"Last activity",
"NAME":"Ad",
"EMAIL":"Elektron poçt",
"PHONE_NUMBER":"Telefon nömrəsi",
"COMPANY":"Şirkət",
"COUNTRY":"Ölkə",
"CITY":"Şəhər",
"LAST_ACTIVITY":"Son fəaliyyət",
"CREATED_AT":"Created at"
}
},
"ORDER":{
"LABEL":"Ordering",
"OPTIONS":{
"ASCENDING":"Ascending",
"ASCENDING":"Artan sıra ilə",
"DESCENDING":"Descending"
}
},
@@ -358,17 +358,17 @@
"CREATE_SEGMENT":{
"TITLE":"Do you want to save this filter?",
"CONFIRM":"Save filter",
"LABEL":"Name",
"LABEL":"Ad",
"PLACEHOLDER":"Enter the name of the filter",
"ERROR":"Enter a valid name",
"ERROR":"Etibarlı ad daxil edin",
"SUCCESS_MESSAGE":"Filter saved successfully",
"ERROR_MESSAGE":"Unable to save filter. Please try again later."
},
"DELETE_SEGMENT":{
"TITLE":"Confirm Deletion",
"DESCRIPTION":"Are you sure you want to delete this filter?",
"UPDATE_ERROR":"Unable to update attribute. Please try again later",
"DELETE_ERROR":"Unable to delete attribute. Please try again later"
"DELETE_ERROR":"Xüsusiyyəti silmək mümkün olmadı. Zəhmət olmasa, bir az sonra yenidən cəhd edin"
}
},
"MERGE":{
"TITLE":"Merge contact",
"DESCRIPTION":"Combine two profiles into one, including all attributes and conversations. In case of conflict, the primary contact’s attributes will take precedence.",
"PRIMARY":"Primary contact",
"DESCRIPTION":"İki profili bütün atributlar və söhbətlər daxil olmaqla birləşdirin. Ziddiyyət olduqda, əsas əlaqənin atributları üstünlük təşkil edəcək.",
"PRIMARY":"Əsas əlaqə",
"PRIMARY_HELP_LABEL":"To be saved",
"PRIMARY_REQUIRED_ERROR":"Please select a contact to merge with before proceeding",
"PRIMARY_REQUIRED_ERROR":"Davam etməzdən əvvəl birləşdirmək üçün əlaqə seçin",
"PARENT":"To be merged",
"PARENT_HELP_LABEL":"To be deleted",
"EMPTY_STATE":"No contacts found",
"PLACEHOLDER":"Search for primary contact",
"SEARCH_PLACEHOLDER":"Search for a contact",
"PLACEHOLDER":"Əsas əlaqəni axtar",
"SEARCH_PLACEHOLDER":"Əlaqə axtar",
"SEARCH_ERROR_MESSAGE":"Could not search for contacts. Please try again later.",
"SUCCESS_MESSAGE":"Contact merged successfully",
"ERROR_MESSAGE":"Could not merge contacts, try again!",
"ERROR_MESSAGE":"Əlaqələri birləşdirmək mümkün olmadı, yenidən cəhd edin!",
"IS_SEARCHING":"Searching...",
"BUTTONS":{
"CANCEL":"Cancel",
"CANCEL":"Ləğv et",
"CONFIRM":"Merge contact"
}
},
"NOTES":{
"PLACEHOLDER":"Add a note",
"WROTE":"wrote",
"YOU":"You",
"PLACEHOLDER":"Qeyd əlavə et",
"WROTE":"yazdı",
"YOU":"Siz",
"SAVE":"Save note",
"ADD_NOTE":"Add contact note",
"EXPAND":"Expand",
"EXPAND":"Genişləndir",
"COLLAPSE":"Collapse",
"NO_NOTES":"No notes, you can add notes from the contact details page.",
"EMPTY_STATE":"There are no notes associated to this contact. You can add a note by typing in the box above.",
"CONVERSATION_EMPTY_STATE":"There are no notes yet. Use the Add note button to create one."
"CONVERSATION_EMPTY_STATE":"Hələ qeydlər yoxdur. Yeni qeyd yaratmaq üçün Qeyd əlavə et düyməsini istifadə edin."
}
},
"EMPTY_STATE":{
"TITLE":"No contacts found in this account",
"SUBTITLE":"Start adding new contacts by clicking on the button below",
"BUTTON_LABEL":"Add contact",
"BUTTON_LABEL":"Əlaqə əlavə et",
"SEARCH_EMPTY_STATE_TITLE":"No contacts matches your search 🔍",
"LIST_EMPTY_STATE_TITLE":"No contacts available in this view 📋",
"ACTIVE_EMPTY_STATE_TITLE":"No contacts are active at the moment 🌙"
"LIST_EMPTY_STATE_TITLE":"Bu baxışda əlaqə mövcud deyil 📋",
"ACTIVE_EMPTY_STATE_TITLE":"Hazırda aktiv əlaqə yoxdur 🌙"
},
"LOAD_MORE":"Load more"
},
@@ -585,12 +585,12 @@
"ASSIGN_LABELS_FAILED":"Failed to assign labels",
"DESCRIPTION":"Select the labels you want to add to the selected contacts.",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER":"This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER":"Bu Instagram hesabı yeni Instagram kanalının daxil olan qutusuna köçürülüb. Bütün yeni mesajlar orada görünəcək. Bu söhbətdən artıq mesaj göndərə bilməyəcəksiniz.",
"MESSAGE":"Bu əməliyyatı geri qaytara bilməzsiniz",
"DELETE":"Sil",
"CANCEL":"Ləğv et"
}
},
"SIDEBAR":{
"CONTACT":"Contact",
"CONTACT":"Əlaqə",
"COPILOT":"Copilot"
},
"VOICE_WIDGET":{
"INCOMING_CALL":"Incoming call",
"OUTGOING_CALL":"Outgoing call",
"CALL_IN_PROGRESS":"Call in progress",
"NOT_ANSWERED_YET":"Not answered yet",
"HANDLED_IN_ANOTHER_TAB":"Being handled in another tab",
"REJECT_CALL":"Reject",
"JOIN_CALL":"Join call",
"END_CALL":"End call"
"INCOMING_CALL":"Gələn zəng",
"OUTGOING_CALL":"Gedən zəng",
"CALL_IN_PROGRESS":"Zəng davam edir",
"NOT_ANSWERED_YET":"Hələ cavab verilməyib",
"HANDLED_IN_ANOTHER_TAB":"Başqa sekmədə işlənir",
"REJECT_CALL":"İmtina et",
"JOIN_CALL":"Zəngə qoşul",
"END_CALL":"Zəngi bitir"
}
},
"EMAIL_TRANSCRIPT":{
"TITLE":"Send conversation transcript",
"DESC":"Send a copy of the conversation transcript to the specified email address",
"SUBMIT":"Submit",
"CANCEL":"Cancel",
"SEND_EMAIL_SUCCESS":"The chat transcript was sent successfully",
"SEND_EMAIL_ERROR":"There was an error, please try again",
"SEND_EMAIL_PAYMENT_REQUIRED":"Email transcript is not available on your current plan. Please upgrade to use this feature.",
"TITLE":"Söhbət transkriptini göndər",
"DESC":"Söhbət transkriptinin surətini göstərilən e-poçt ünvanına göndərin",
"SUBMIT":"Təsdiqlə",
"CANCEL":"Ləğv et",
"SEND_EMAIL_SUCCESS":"Söhbət yazısı uğurla göndərildi",
"SEND_EMAIL_ERROR":"Xəta baş verdi, zəhmət olmasa yenidən cəhd edin",
"SEND_EMAIL_PAYMENT_REQUIRED":"Cari planınızda e-poçt yazışması mövcud deyil. Bu funksiyanı istifadə etmək üçün lütfən planınızı yüksəldin.",
"FORM":{
"SEND_TO_CONTACT":"Send the transcript to the customer",
"SEND_TO_AGENT":"Send the transcript to the assigned agent",
"SEND_TO_OTHER_EMAIL_ADDRESS":"Send the transcript to another email address",
"SEND_TO_CONTACT":"Yazını müştəriyə göndər",
"SEND_TO_AGENT":"Mətni təyin olunmuş agentə göndər",
"SEND_TO_OTHER_EMAIL_ADDRESS":"Yazını başqa e-poçt ünvanına göndər",
"EMAIL":{
"PLACEHOLDER":"Enter an email address",
"ERROR":"Please enter a valid email address"
"PLACEHOLDER":"E-poçt ünvanı daxil edin",
"ERROR":"Zəhmət olmasa, düzgün e-poçt ünvanı daxil edin"
}
}
},
"ONBOARDING":{
"TITLE":"Hey 👋, Welcome to {installationName}!",
"DESCRIPTION":"Thanks for signing up. We want you to get the most out of {installationName}. Here are a few things you can do in {installationName} to make the experience delightful.",
"GREETING_MORNING":"👋 Good morning, {name}. Welcome to {installationName}.",
"GREETING_AFTERNOON":"👋 Good afternoon, {name}. Welcome to {installationName}.",
"GREETING_EVENING":"👋 Good evening, {name}. Welcome to {installationName}.",
"READ_LATEST_UPDATES":"Read our latest updates",
"TITLE":"Salam 👋, {installationName}-ə xoş gəlmisiniz!",
"DESCRIPTION":"Qeydiyyatdan keçdiyiniz üçün təşəkkür edirik. {installationName}-dən maksimum faydalanmağınızı istəyirik. Təcrübəni xoş etmək üçün {installationName}-də edə biləcəyiniz bir neçə şey var.",
"GREETING_MORNING":"👋 Sabahınız xeyir, {name}. {installationName}-ə xoş gəlmisiniz.",
"GREETING_AFTERNOON":"👋 Günortanız xeyir, {name}. {installationName}-ə xoş gəlmisiniz.",
"GREETING_EVENING":"👋 Axşamınız xeyir, {name}. {installationName}-ə xoş gəlmisiniz.",
"READ_LATEST_UPDATES":"Ən son yeniliklərimizi oxuyun",
"ALL_CONVERSATION":{
"TITLE":"All your conversations in one place",
"DESCRIPTION":"View all the conversations from your customers in one single dashboard. You can filter the conversations by the incoming channel, label and status.",
"NEW_LINK":"Click here to create an inbox"
"TITLE":"Bütün söhbətləriniz bir yerdə",
"DESCRIPTION":"Müştərilərinizdən olan bütün söhbətləri tək bir paneldə görün. Söhbətləri daxil olan kanal, etiket və vəziyyətə görə süzgəcdən keçirə bilərsiniz.",
"NEW_LINK":"Qutunu yaratmaq üçün bura klikləyin"
},
"TEAM_MEMBERS":{
"TITLE":"Invite your team members",
"DESCRIPTION":"Since you are getting ready to talk to your customer, bring in your teammates to assist you. You can invite your teammates by adding their email addresses to the agent list.",
"NEW_LINK":"Click here to invite a team member"
"TITLE":"Komanda üzvlərinizi dəvət edin",
"DESCRIPTION":"Müştərinizlə danışmağa hazırlaşdığınız üçün, sizə kömək etmək üçün komanda üzvlərinizi dəvət edin. Komanda üzvlərinizi agent siyahısına onların e-poçt ünvanlarını əlavə etməklə dəvət edə bilərsiniz.",
"NEW_LINK":"Komanda üzvünü dəvət etmək üçün buraya klikləyin"
},
"LABELS":{
"TITLE":"Organize conversations with labels",
"DESCRIPTION":"Labels provide an easier way to categorize your conversation. Create some labels like #support-enquiry, #billing-question etc., so that you can use them in a conversation later.",
"NEW_LINK":"Click here to create tags"
"TITLE":"Söhbətləri etiketlərlə təşkil edin",
"DESCRIPTION":"Etiketlər söhbətinizi kateqoriyalara ayırmağı asanlaşdırır. Sonra söhbətdə istifadə etmək üçün #support-enquiry, #billing-question və s. kimi bəzi etiketlər yaradın.",
"NEW_LINK":"Etiket yaratmaq üçün buraya klikləyin"
},
"CANNED_RESPONSES":{
"TITLE":"Create canned responses",
"DESCRIPTION":"Pre-written quick reply templates help you quickly respond to a conversation. Agents can type the '/' character followed by the shortcode to insert a response.",
"NEW_LINK":"Click here to create a canned response"
"TITLE":"Hazır cavablar yaradın",
"DESCRIPTION":"Əvvəlcədən yazılmış sürətli cavab şablonları söhbətə tez cavab verməyinizə kömək edir. Agentlər cavaba daxil etmək üçün '/' simvolunu və sonra qısa kodu yaza bilərlər.",
"NEW_LINK":"Sürətli cavab yaratmaq üçün bura klikləyin"
}
},
"CONVERSATION_SIDEBAR":{
"ASSIGNEE_LABEL":"Assigned Agent",
"SELF_ASSIGN":"Assign to me",
"TEAM_LABEL":"Assigned Team",
"ASSIGNEE_LABEL":"Təyin olunmuş agent",
"SELF_ASSIGN":"Mənə təyin et",
"TEAM_LABEL":"Təyin olunmuş komanda",
"SELECT":{
"PLACEHOLDER":"None"
"PLACEHOLDER":"Heç biri"
},
"ACCORDION":{
"CONTACT_DETAILS":"Contact Details",
"CONVERSATION_ACTIONS":"Conversation Actions",
"CONVERSATION_LABELS":"Conversation Labels",
"CONVERSATION_INFO":"Conversation Information",
"CONTACT_NOTES":"Contact Notes",
"CONTACT_ATTRIBUTES":"Contact Attributes",
"PREVIOUS_CONVERSATION":"Previous Conversations",
"MACROS":"Macros",
"LINEAR_ISSUES":"Linked Linear Issues",
"SHOPIFY_ORDERS":"Shopify Orders"
"CONTACT_DETAILS":"Əlaqə Məlumatları",
"CONVERSATION_ACTIONS":"Söhbət Əməliyyatları",
"CONVERSATION_LABELS":"Söhbət Etiketləri",
"CONVERSATION_INFO":"Söhbət Məlumatları",
"CONTACT_NOTES":"Əlaqə Qeydləri",
"CONTACT_ATTRIBUTES":"Əlaqə Xüsusiyyətləri",
"PREVIOUS_CONVERSATION":"Əvvəlki Söhbətlər",
"MACROS":"Makrolar",
"LINEAR_ISSUES":"Əlaqəli Linear məsələlər",
"SHOPIFY_ORDERS":"Shopify Sifarişləri"
},
"SHOPIFY":{
"ORDER_ID":"Order #{id}",
"ERROR":"Error loading orders",
"NO_SHOPIFY_ORDERS":"No orders found",
"ORDER_ID":"Sifariş #{id}",
"ERROR":"Sifarişlərin yüklənməsində xəta",
"NO_SHOPIFY_ORDERS":"Sifariş tapılmadı",
"FINANCIAL_STATUS":{
"PENDING":"Pending",
"AUTHORIZED":"Authorized",
"PARTIALLY_PAID":"Partially Paid",
"PAID":"Paid",
"PARTIALLY_REFUNDED":"Partially Refunded",
"REFUNDED":"Refunded",
"VOIDED":"Voided"
"PENDING":"Gözləmədə",
"AUTHORIZED":"Təsdiqlənmiş",
"PARTIALLY_PAID":"Qismən ödənilmiş",
"PAID":"Ödənilib",
"PARTIALLY_REFUNDED":"Qismən Geri Ödənilib",
"REFUNDED":"Geri Ödənilib",
"VOIDED":"Ləğv Edilib"
},
"FULFILLMENT_STATUS":{
"FULFILLED":"Fulfilled",
"PARTIALLY_FULFILLED":"Partially Fulfilled",
"UNFULFILLED":"Unfulfilled"
"FULFILLED":"Yerinə Yetirilib",
"PARTIALLY_FULFILLED":"Qismən Yerinə Yetirilib",
"UNFULFILLED":"Yerinə Yetirilməyib"
}
}
},
"CONVERSATION_CUSTOM_ATTRIBUTES":{
"ADD_BUTTON_TEXT":"Create attribute",
"NO_RECORDS_FOUND":"No attributes found",
"ADD_BUTTON_TEXT":"Xüsusiyyət yaradın",
"NO_RECORDS_FOUND":"Heç bir atribut tapılmadı",
"UPDATE":{
"SUCCESS":"Attribute updated successfully",
"ERROR":"Unable to update attribute. Please try again later"
"SUCCESS":"Xüsusiyyət uğurla yeniləndi",
"ERROR":"Xüsusiyyət yenilənə bilmədi. Zəhmət olmasa, bir az sonra yenidən cəhd edin"
},
"ADD":{
"TITLE":"Add",
"SUCCESS":"Attribute added successfully",
"ERROR":"Unable to add attribute. Please try again later"
"TITLE":"Əlavə et",
"SUCCESS":"Xüsusiyyət uğurla əlavə edildi",
"ERROR":"Xüsusiyyət əlavə edilə bilmədi. Zəhmət olmasa, bir az sonra yenidən cəhd edin"
},
"DELETE":{
"SUCCESS":"Attribute deleted successfully",
"ERROR":"Unable to delete attribute. Please try again later"
"SUCCESS":"Xüsusiyyət uğurla silindi",
"ERROR":"Atributu silmək mümkün olmadı. Zəhmət olmasa, bir az sonra yenidən cəhd edin"
},
"ATTRIBUTE_SELECT":{
"TITLE":"Add attributes",
"PLACEHOLDER":"Search attributes",
"NO_RESULT":"No attributes found"
"TITLE":"Atributlar əlavə et",
"PLACEHOLDER":"Atributlarda axtar",
"NO_RESULT":"Heç bir atribut tapılmadı"
}
},
"EMAIL_HEADER":{
"FROM":"From",
"TO":"To",
"BCC":"Bcc",
"CC":"Cc",
"SUBJECT":"Subject",
"EXPAND":"Expand email"
"FROM":"Kimdən",
"TO":"Kimə",
"BCC":"Gizli nüsxə",
"CC":"Nüsxə",
"SUBJECT":"Mövzu",
"EXPAND":"E-poçtu genişləndir"
},
"CONVERSATION_PARTICIPANTS":{
"SIDEBAR_MENU_TITLE":"Participating",
"SIDEBAR_TITLE":"Conversation participants",
"NO_RECORDS_FOUND":"No results found",
"ADD_PARTICIPANTS":"Select participants",
"REMANING_PARTICIPANTS_TEXT":"+{count} others",
"REMANING_PARTICIPANT_TEXT":"+{count} other",
"TOTAL_PARTICIPANTS_TEXT":"{count} people are participating.",
"TOTAL_PARTICIPANT_TEXT":"{count} person is participating.",
"SIDEBAR_MENU_TITLE":"İştirak edənlər",
"SIDEBAR_TITLE":"Söhbət iştirakçıları",
"NO_RECORDS_FOUND":"Nəticə tapılmadı",
"ADD_PARTICIPANTS":"İştirakçıları seçin",
"REMANING_PARTICIPANTS_TEXT":"+{count} digər",
"REMANING_PARTICIPANT_TEXT":"+{count} digər",
"TOTAL_PARTICIPANTS_TEXT":"{count} nəfər iştirak edir.",
"TOTAL_PARTICIPANT_TEXT":"{count} nəfər iştirak edir.",
"NO_PARTICIPANTS_TEXT":"No one is participating!.",
"WATCH_CONVERSATION":"Join conversation",
"YOU_ARE_WATCHING":"You are participating",
"WATCH_CONVERSATION":"Söhbətə qoşulun",
"YOU_ARE_WATCHING":"Siz iştirak edirsiniz",
"API":{
"ERROR_MESSAGE":"Could not update, try again!",
"SUCCESS_MESSAGE":"Participants updated!"
"ERROR_MESSAGE":"Yenilənmədi, yenidən cəhd edin!",
"SUCCESS_MESSAGE":"İştirakçılar yeniləndi!"
}
},
"TRANSLATE_MODAL":{
"TITLE":"View translated content",
"TITLE":"Tərcümə edilmiş məzmunu göstər",
"DESC":"You can view the translated content in each langauge.",
"ORIGINAL_CONTENT":"Original Content",
"TRANSLATED_CONTENT":"Translated Content",
"NO_TRANSLATIONS_AVAILABLE":"No translations are available for this content"
"ORIGINAL_CONTENT":"Orijinal məzmun",
"TRANSLATED_CONTENT":"Tərcümə edilmiş məzmun",
"NO_TRANSLATIONS_AVAILABLE":"Bu məzmun üçün tərcümə mövcud deyil"
},
"TYPING":{
"ONE":"{user} is typing",
"TWO":"{user} and {secondUser} are typing",
"MULTIPLE":"{user} and {count} others are typing"
"ONE":"{user} yazır",
"TWO":"{user} və {secondUser} yazırlar",
"MULTIPLE":"{user} və {count} başqası yazırlar"
},
"COPILOT":{
"TRY_THESE_PROMPTS":"Try these prompts"
"TRY_THESE_PROMPTS":"Bu təklifləri sınayın"
},
"GALLERY_VIEW":{
"ERROR_DOWNLOADING":"Unable to download attachment. Please try again"
"ERROR_DOWNLOADING":"Əlavəni yükləmək mümkün olmadı. Zəhmət olmasa yenidən cəhd edin"
"IFRAME_ERROR":"URL boş və ya düzgün deyil. Məzmun göstərilə bilmir.",
"OPEN_ARTICLE_SEARCH":"Kömək Mərkəzindən məqalə əlavə et",
"SUCCESS_ARTICLE_INSERTED":"Məqalə uğurla əlavə edildi",
"PREVIEW_LINK":"Məqaləni önizləyin",
"CANCEL":"Bağla",
"BACK":"Geri",
"BACK_RESULTS":"Nəticələrə qayıdın"
},
"UPGRADE_PAGE":{
"TITLE":"Help Center",
"DESCRIPTION":"Create user-friendly self-service portals. Help your users to access the articles and get support 24/7. Upgrade your subscription to enable this feature.",
"SELF_HOSTED_DESCRIPTION":"Create user-friendly self-service portals. Help your users to access the articles and get support 24/7. Please contact your administrator to enable this feature.",
"TITLE":"Kömək Mərkəzi",
"DESCRIPTION":"İstifadəçi dostu özünə xidmət portalları yaradın. İstifadəçilərinizə məqalələrə daxil olmaq və 24/7 dəstək almaqda kömək edin. Bu funksiyanı aktivləşdirmək üçün abunəliyinizi yüksəldin.",
"SELF_HOSTED_DESCRIPTION":"İstifadəçi dostu özünə xidmət portalları yaradın. İstifadəçilərinizə məqalələrə daxil olmaq və 24/7 dəstək almaqda kömək edin. Zəhmət olmasa, bu funksiyanı aktivləşdirmək üçün administratorunuzla əlaqə saxlayın.",
"BUTTON":{
"LEARN_MORE":"Learn more",
"UPGRADE":"Upgrade"
"LEARN_MORE":"Ətraflı öyrən",
"UPGRADE":"Yenilə"
},
"FEATURES":{
"PORTALS":{
"TITLE":"Multiple portals",
"DESCRIPTION":"Create multiple help center portals for different products using the same account."
"TITLE":"Çoxsaylı portallar",
"DESCRIPTION":"Eyni hesabdan istifadə edərək müxtəlif məhsullar üçün çoxsaylı kömək mərkəzi portalları yaradın."
},
"LOCALES":{
"TITLE":"Full support for locales",
"DESCRIPTION":"Localize the portal in your language. We support all locales and allow translations for every article."
"TITLE":"Dil variantlarının tam dəstəyi",
"DESCRIPTION":"Portalı öz dilinizə uyğunlaşdırın. Bütün dil variantlarını dəstəkləyirik və hər məqalə üçün tərcümələrə imkan veririk."
},
"SEO":{
"TITLE":"SEO-friendly design",
"DESCRIPTION":"Customize your meta tags to improve your visibility on search engines with our SEO-friendly pages."
"TITLE":"SEO-ya uyğun dizayn",
"DESCRIPTION":"Meta teqlərinizi fərdiləşdirərək axtarış motorlarında görünürlüğünüzü SEO dostu səhifələrimizlə artırın."
},
"API":{
"TITLE":"Full API support",
"DESCRIPTION":"Use the portal as a headless CMS with third party front-end frameworks using our APIs."
"TITLE":"Tam API dəstəyi",
"DESCRIPTION":"Portaldan üçüncü tərəf ön çərçivələri ilə başsız CMS kimi istifadə etmək üçün API-lərimizdən yararlanın."
"DESCRIPTION":"Easily track agent performance with key metrics such as conversations, response times, resolution times, and resolved cases. Click an agent's name to learn more.",
"LOADING_CHART":"Loading chart data...",
"NO_ENOUGH_DATA":"We've not received enough data points to generate report, Please try again later.",
"NO_ENOUGH_DATA":"Hesabat yaratmaq üçün kifayət qədər məlumat nöqtəsi almamışıq, zəhmət olmasa sonra yenidən cəhd edin.",
"INFO_TEXT":"Total number of conversations used for computation:",
"TOOLTIP_TEXT":"First Response Time is {metricValue} (based on {conversationCount} conversations)"
"NAME":"İlk Cavab Vaxtı",
"DESC":"( Orta )",
"INFO_TEXT":"Hesablama üçün istifadə olunan ümumi söhbət sayı:",
"TOOLTIP_TEXT":"İlk cavab vaxtı {metricValue}-dir ({conversationCount} söhbət əsasında)"
},
"RESOLUTION_TIME":{
"NAME":"Resolution Time",
"DESC":"( Avg )",
"INFO_TEXT":"Total number of conversations used for computation:",
"TOOLTIP_TEXT":"Resolution Time is {metricValue} (based on {conversationCount} conversations)"
"NAME":"Həll Vaxtı",
"DESC":"( Orta )",
"INFO_TEXT":"Hesablama üçün istifadə olunan ümumi söhbət sayı:",
"TOOLTIP_TEXT":"Həll vaxtı {metricValue}-dir ({conversationCount} söhbət əsasında)"
},
"RESOLUTION_COUNT":{
"NAME":"Resolution Count",
"DESC":"( Total )"
"NAME":"Həll Sayı",
"DESC":"( Cəmi )"
}
},
"DATE_RANGE":[
{
"id":0,
"name":"Last 7 days"
"name":"Son 7 gün"
},
{
"id":1,
"name":"Last 30 days"
"name":"Son 30 gün"
},
{
"id":2,
"name":"Last 3 months"
"name":"Son 3 ay"
},
{
"id":3,
"name":"Last 6 months"
"name":"Son 6 ay"
},
{
"id":4,
"name":"Last year"
"name":"Keçən il"
},
{
"id":5,
@@ -195,15 +195,15 @@
}
],
"CUSTOM_DATE_RANGE":{
"CONFIRM":"Apply",
"CONFIRM":"Tətbiq et",
"PLACEHOLDER":"Select date range"
}
},
"LABEL_REPORTS":{
"HEADER":"Labels Overview",
"DESCRIPTION":"Track label performance with key metrics including conversations, response times, resolution times, and resolved cases. Click a label name for detailed insights.",
"DESCRIPTION":"Nişan nəticələrini söhbətlər, cavab müddəti, həll müddəti və həll olunmuş hallar üzrə yoxlayın. Ətraflı məlumat üçün nişan adını açın.",
"LOADING_CHART":"Loading chart data...",
"NO_ENOUGH_DATA":"We've not received enough data points to generate report, Please try again later.",
"NO_ENOUGH_DATA":"Hesabat yaratmaq üçün kifayət qədər məlumat nöqtəsi almamışıq, zəhmət olmasa bir az sonra yenidən cəhd edin.",
"INFO_TEXT":"Total number of conversations used for computation:",
"TOOLTIP_TEXT":"First Response Time is {metricValue} (based on {conversationCount} conversations)"
"NAME":"İlk Cavab Vaxtı",
"DESC":"( Orta )",
"INFO_TEXT":"Hesablama üçün istifadə olunan ümumi söhbət sayı:",
"TOOLTIP_TEXT":"İlk cavab vaxtı {metricValue}-dir ({conversationCount} söhbət əsasında)"
},
"RESOLUTION_TIME":{
"NAME":"Resolution Time",
"DESC":"( Avg )",
"INFO_TEXT":"Total number of conversations used for computation:",
"TOOLTIP_TEXT":"Resolution Time is {metricValue} (based on {conversationCount} conversations)"
"NAME":"Həll Vaxtı",
"DESC":"( Orta )",
"INFO_TEXT":"Hesablama üçün istifadə olunan ümumi söhbət sayı:",
"TOOLTIP_TEXT":"Həll vaxtı {metricValue}-dir ({conversationCount} söhbətə əsaslanır)"
},
"RESOLUTION_COUNT":{
"NAME":"Resolution Count",
"DESC":"( Total )"
"NAME":"Həll Sayı",
"DESC":"( Cəmi )"
}
},
"DATE_RANGE":[
{
"id":0,
"name":"Last 7 days"
"name":"Son 7 gün"
},
{
"id":1,
"name":"Last 30 days"
"name":"Son 30 gün"
},
{
"id":2,
"name":"Last 3 months"
"name":"Son 3 ay"
},
{
"id":3,
"name":"Last 6 months"
"name":"Son 6 ay"
},
{
"id":4,
"name":"Last year"
"name":"Keçən il"
},
{
"id":5,
"name":"Custom date range"
"name":"Xüsusi tarix aralığı"
}
],
"CUSTOM_DATE_RANGE":{
"CONFIRM":"Apply",
"CONFIRM":"Tətbiq et",
"PLACEHOLDER":"Select date range"
}
},
@@ -276,84 +276,84 @@
"HEADER":"Inbox Overview",
"DESCRIPTION":"Quickly view your inbox performance with key metrics like conversations, response times, resolution times, and resolved cases—all in one place. Click an inbox name for more details.",
"LOADING_CHART":"Loading chart data...",
"NO_ENOUGH_DATA":"We've not received enough data points to generate report, Please try again later.",
"NO_ENOUGH_DATA":"Hesabat yaratmaq üçün kifayət qədər məlumat nöqtəsi almamışıq, zəhmət olmasa bir az sonra yenidən cəhd edin.",
"INFO_TEXT":"Total number of conversations used for computation:",
"TOOLTIP_TEXT":"First Response Time is {metricValue} (based on {conversationCount} conversations)"
"NAME":"İlk Cavab Vaxtı",
"DESC":"( Orta )",
"INFO_TEXT":"Hesablama üçün istifadə olunan ümumi söhbət sayı:",
"TOOLTIP_TEXT":"İlk cavab vaxtı {metricValue}-dir ({conversationCount} söhbətə əsaslanır)"
},
"RESOLUTION_TIME":{
"NAME":"Resolution Time",
"DESC":"( Avg )",
"INFO_TEXT":"Total number of conversations used for computation:",
"TOOLTIP_TEXT":"Resolution Time is {metricValue} (based on {conversationCount} conversations)"
"NAME":"Həll Vaxtı",
"DESC":"( Orta )",
"INFO_TEXT":"Hesablama üçün istifadə olunan ümumi söhbət sayı:",
"TOOLTIP_TEXT":"Həll vaxtı {metricValue}-dir ({conversationCount} söhbətə əsaslanır)"
},
"RESOLUTION_COUNT":{
"NAME":"Resolution Count",
"DESC":"( Total )"
"NAME":"Həll Sayı",
"DESC":"( Cəmi )"
}
},
"DATE_RANGE":[
{
"id":0,
"name":"Last 7 days"
"name":"Son 7 gün"
},
{
"id":1,
"name":"Last 30 days"
"name":"Son 30 gün"
},
{
"id":2,
"name":"Last 3 months"
"name":"Son 3 ay"
},
{
"id":3,
"name":"Last 6 months"
"name":"Son 6 ay"
},
{
"id":4,
"name":"Last year"
"name":"Keçən il"
},
{
"id":5,
"name":"Custom date range"
"name":"Xüsusi tarix aralığı"
}
],
"CUSTOM_DATE_RANGE":{
"CONFIRM":"Apply",
"CONFIRM":"Tətbiq et",
"PLACEHOLDER":"Select date range"
}
},
"TEAM_REPORTS":{
"HEADER":"Team Overview",
"DESCRIPTION":"Get a snapshot of your team's performance with essential metrics, including conversations, response times, resolution times, and resolved cases. Click a team name for more details.",
"DESCRIPTION":"Komanda nəticələrini söhbətlər, cavab müddəti, həll müddəti və həll olunmuş hallar üzrə yoxlayın. Ətraflı məlumat üçün komanda adını açın.",
"LOADING_CHART":"Loading chart data...",
"NO_ENOUGH_DATA":"We've not received enough data points to generate report, Please try again later.",
"NO_ENOUGH_DATA":"Hesabat yaratmaq üçün kifayət qədər məlumat nöqtəsi almamışıq, zəhmət olmasa sonra yenidən cəhd edin.",
"DOWNLOAD_TEAM_REPORTS":"Download team reports",
"FILTER_DROPDOWN_LABEL":"Select Team",
"FILTER_DROPDOWN_LABEL":"Komandanı seçin",
"FILTERS":{
"ADD_FILTER":"Add filter",
"CLEAR_ALL":"Clear all",
@@ -364,54 +364,54 @@
},
"METRICS":{
"CONVERSATIONS":{
"NAME":"Conversations",
"DESC":"( Total )"
"NAME":"Söhbətlər",
"DESC":"( Ümumi )"
},
"INCOMING_MESSAGES":{
"NAME":"Incoming Messages",
"DESC":"( Total )"
"DESC":"( Cəmi )"
},
"OUTGOING_MESSAGES":{
"NAME":"Outgoing Messages",
"DESC":"( Total )"
"DESC":"( Cəmi )"
},
"FIRST_RESPONSE_TIME":{
"NAME":"First Response Time",
"DESC":"( Avg )",
"INFO_TEXT":"Total number of conversations used for computation:",
"TOOLTIP_TEXT":"First Response Time is {metricValue} (based on {conversationCount} conversations)"
"NAME":"İlk Cavab Vaxtı",
"DESC":"( Orta )",
"INFO_TEXT":"Hesablama üçün istifadə olunan ümumi söhbət sayı:",
"TOOLTIP_TEXT":"İlk cavab vaxtı {metricValue}-dir ({conversationCount} söhbətə əsaslanır)"
},
"RESOLUTION_TIME":{
"NAME":"Resolution Time",
"DESC":"( Avg )",
"INFO_TEXT":"Total number of conversations used for computation:",
"TOOLTIP_TEXT":"Resolution Time is {metricValue} (based on {conversationCount} conversations)"
"NAME":"Həll Vaxtı",
"DESC":"( Orta )",
"INFO_TEXT":"Hesablama üçün istifadə olunan ümumi söhbət sayı:",
"TOOLTIP_TEXT":"Həll vaxtı {metricValue}-dir ({conversationCount} söhbətə əsaslanır)"
},
"RESOLUTION_COUNT":{
"NAME":"Resolution Count",
"DESC":"( Total )"
"NAME":"Həll Sayı",
"DESC":"( Ümumi )"
}
},
"DATE_RANGE":[
{
"id":0,
"name":"Last 7 days"
"name":"Son 7 gün"
},
{
"id":1,
"name":"Last 30 days"
"name":"Son 30 gün"
},
{
"id":2,
"name":"Last 3 months"
"name":"Son 3 ay"
},
{
"id":3,
"name":"Last 6 months"
"name":"Son 6 ay"
},
{
"id":4,
"name":"Last year"
"name":"Keçən il"
},
{
"id":5,
@@ -419,13 +419,13 @@
}
],
"CUSTOM_DATE_RANGE":{
"CONFIRM":"Apply",
"CONFIRM":"Tətbiq et",
"PLACEHOLDER":"Select date range"
}
},
"CSAT_REPORTS":{
"HEADER":"CSAT Reports",
"NO_RECORDS":"No responses yet",
"HEADER":"CSAT Hesabatları",
"NO_RECORDS":"Hələ cavab yoxdur",
"NO_RECORDS_DESCRIPTION":"CSAT survey responses will appear here once customers start providing feedback.",
"DOWNLOAD":"Download CSAT Reports",
"DOWNLOAD_FAILED":"Failed to download CSAT Reports",
@@ -435,7 +435,7 @@
"NO_FILTER":"No filters available",
"INPUT_PLACEHOLDER":{
"AGENTS":"Search agents",
"INBOXES":"Search inboxes",
"INBOXES":"Qutuları axtar",
"TEAMS":"Search teams",
"RATINGS":"Search ratings"
},
@@ -443,53 +443,53 @@
"LABEL":"Agent"
},
"INBOXES":{
"LABEL":"Inbox"
"LABEL":"Gələn qutu"
},
"TEAMS":{
"LABEL":"Team"
"LABEL":"Komanda"
},
"RATINGS":{
"LABEL":"Rating"
"LABEL":"Qiymətləndirmə"
}
},
"TABLE":{
"HEADER":{
"CONTACT_NAME":"Contact",
"CONTACT_NAME":"Əlaqə",
"AGENT_NAME":"Agent",
"RATING":"Rating",
"RATING":"Qiymətləndirmə",
"FEEDBACK_TEXT":"Feedback comment",
"CONVERSATION":"Conversation",
"CUSTOMER":"Customer",
"RESPONSE":"Response",
"HANDLED_BY":"Handled by"
"CONVERSATION":"Söhbət",
"CUSTOMER":"Müştəri",
"RESPONSE":"Cavab",
"HANDLED_BY":"Tərəfindən idarə olunur"
},
"UNKNOWN_CUSTOMER":"Unknown customer"
"UNKNOWN_CUSTOMER":"Naməlum müştəri"
},
"NO_AGENT":"No assigned agent",
"NO_FEEDBACK":"No feedback provided",
"NO_AGENT":"Təyin olunmuş agent yoxdur",
"NO_FEEDBACK":"Rəy verilməyib",
"METRIC":{
"TOTAL_RESPONSES":{
"LABEL":"Total responses",
"TOOLTIP":"Total number of responses collected"
"LABEL":"Cəmi cavablar",
"TOOLTIP":"Toplanmış cavabların ümumi sayı"
},
"SATISFACTION_SCORE":{
"LABEL":"Satisfaction score",
"TOOLTIP":"Total number of positive responses / Total number of responses * 100"
"LABEL":"Məmnunluq balı",
"TOOLTIP":"Müsbət cavabların ümumi sayı / Cavabların ümumi sayı * 100"
},
"RESPONSE_RATE":{
"LABEL":"Response rate",
"LABEL":"Cavab faizi",
"TOOLTIP":"Total number of responses / Total number of CSAT survey messages sent * 100"
},
"RATING_DISTRIBUTION":"Rating distribution"
"RATING_DISTRIBUTION":"Qiymətləndirmə paylanması"
},
"REVIEW_NOTES":{
"TITLE":"Review notes",
"PLACEHOLDER":"Add review notes about this rating...",
"SAVE":"Save",
"CANCEL":"Cancel",
"SAVING":"Saving...",
"TITLE":"Rəy qeydləri",
"PLACEHOLDER":"Bu qiymətləndirmə haqqında rəy qeydləri əlavə edin...",
"SAVE":"Yadda saxla",
"CANCEL":"Ləğv et",
"SAVING":"Yadda saxlanılır...",
"SAVED":"Notes saved successfully",
"SAVE_ERROR":"Failed to save notes",
"SAVE_ERROR":"Qeydləri yadda saxlamaq mümkün olmadı",
"UPDATED_BY":"Updated by {name} {time}",
"UPDATED_BY_LABEL":"Updated by",
"PAYWALL":{
@@ -509,42 +509,42 @@
"TOOLTIP":"Total number of conversations handled by the bot"
},
"TOTAL_RESPONSES":{
"LABEL":"Total Responses",
"LABEL":"Ümumi Cavablar",
"TOOLTIP":"Total number of responses sent by the bot"
},
"RESOLUTION_RATE":{
"LABEL":"Resolution Rate",
"LABEL":"Həll Faizi",
"TOOLTIP":"Total number of conversations resolved by the bot / Total number of conversations handled by the bot * 100"
},
"HANDOFF_RATE":{
"LABEL":"Handoff Rate",
"LABEL":"Təyinat Faizi",
"TOOLTIP":"Total number of conversations handed off to agents / Total number of conversations handled by the bot * 100"
"MESSENGER_SUB_HEAD":"Place this button inside your body tag",
"ALLOWED_DOMAINS":{
"TITLE":"Allowed Domains",
"SUBTITLE":"Add wildcard or regular domains separated by commas (leave blank to allow all), e.g. *.chatwoot.dev, chatwoot.com.",
"PLACEHOLDER":"Enter domains separated by commas (eg: *.chatwoot.dev, chatwoot.com)"
"DESCRIPTION":"Restrict which websites can embed your chat widget. For security, only add domains you own and trust. Add one or more domains separated by commas. Leave blank to allow all domains (not recommended for production).",
"SUBTITLE":"Check this if you embed the widget in iOS or Android apps. Mobile apps don't send domain information, so they would be blocked by domain restrictions unless this is enabled."
},
"IDENTITY_VALIDATION":{
"TITLE":"Identity Validation",
"DESCRIPTION":"Verify user authenticity by generating secure tokens. This prevents unauthorized users from impersonating others in your chat.",
"SECRET_KEY":"Secret Key",
"VIEW_DOCS":"View documentation",
"REQUIRE_LABEL":"Require identity validation for all conversations",
"REQUIRE_DESCRIPTION":"When enabled, users must provide a valid identity token to start conversations. Requests without valid tokens will be rejected."
},
"INBOX_AGENTS":"Агенти",
"INBOX_AGENTS_SUB_TEXT":"Add or remove agents from this inbox",
"EMPTY_LIST":"No assistants found, please create one to get started"
"ASSISTANTS":"Асистенти",
"SWITCH_ASSISTANT":"Превключване между асистенти",
"NEW_ASSISTANT":"Създайте асистент",
"EMPTY_LIST":"Не са намерени асистенти, моля създайте един, за да започнете"
},
"COPILOT":{
"TITLE":"Copilot",
"TRY_THESE_PROMPTS":"Try these prompts",
"PANEL_TITLE":"Get started with Copilot",
"KICK_OFF_MESSAGE":"Need a quick summary, want to check past conversations, or draft a better reply? Copilot’s here to speed things up.",
"PANEL_TITLE":"Започнете с Copilot",
"KICK_OFF_MESSAGE":"Имате нужда от бързо резюме, искате да проверите минали разговори или да създадете по-добър отговор? Copilot е тук, за да ускори нещата.",
"SEND_MESSAGE":"Изпрати съобщение...",
"EMPTY_MESSAGE":"There was an error generating the response. Please try again.",
"LOADER":"Captain is thinking",
"EMPTY_MESSAGE":"Възникна грешка при генериране на отговора. Моля, опитайте отново.",
"LOADER":"Captain мисли",
"YOU":"You",
"USE":"Use this",
"RESET":"Reset",
"SHOW_STEPS":"Show steps",
"SELECT_ASSISTANT":"Select Assistant",
"USE":"Използвай това",
"RESET":"Нулиране",
"SHOW_STEPS":"Покажи стъпки",
"SELECT_ASSISTANT":"Изберете асистент",
"PROMPTS":{
"SUMMARIZE":{
"LABEL":"Summarize this conversation",
"CONTENT":"Summarize the key points discussed between the customer and the support agent, including the customer's concerns, questions, and the solutions or responses provided by the support agent"
"LABEL":"Обобщете този разговор",
"CONTENT":"Обобщете основните точки, обсъдени между клиента и поддържащия агент, включително притесненията на клиента, въпросите и решенията или отговорите, предоставени от агента."
},
"SUGGEST":{
"LABEL":"Suggest an answer",
"CONTENT":"Analyze the customer's inquiry, and draft a response that effectively addresses their concerns or questions. Ensure the reply is clear, concise, and provides helpful information."
"LABEL":"Предложете отговор",
"CONTENT":"Анализирайте запитването на клиента и създайте отговор, който ефективно адресира техните притеснения или въпроси. Уверете се, че отговорът е ясен, кратък и предоставя полезна информация."
},
"RATE":{
"LABEL":"Rate this conversation",
"CONTENT":"Review the conversation to see how well it meets the customer's needs. Share a rating out of 5 based on tone, clarity, and effectiveness."
"LABEL":"Оценете този разговор",
"CONTENT":"Прегледайте разговора, за да видите доколко отговаря на нуждите на клиента. Споделете оценка от 5 въз основа на тон, яснота и ефективност."
},
"HIGH_PRIORITY":{
"LABEL":"High priority conversations",
"CONTENT":"Give me a summary of all high priority open conversations. Include the conversation ID, customer name (if available), last message content, and assigned agent. Group by status if relevant."
"LABEL":"Разговори с висок приоритет",
"CONTENT":"Дайте ми резюме на всички отворени разговори с висок приоритет. Включете ID на разговора, името на клиента (ако е налично), съдържанието на последното съобщение и назначен агент. Групирайте по статус, ако е приложимо."
},
"LIST_CONTACTS":{
"LABEL":"List contacts",
"CONTENT":"Show me the list of top 10 contacts. Include name, email or phone number (if available), last seen time, tags (if any)."
"LABEL":"Списък с контакти",
"CONTENT":"Покажете ми списък с топ 10 контакта. Включете име, имейл или телефонен номер (ако е наличен), време на последно виждане, етикети (ако има такива)."
"ERROR_MESSAGE":"There was an error deleting the documents, please try again."
},
"RELATED_RESPONSES":{
"TITLE":"Related FAQs",
"DESCRIPTION":"These FAQs are generated directly from the document."
@@ -794,6 +806,7 @@
"CUSTOM_TOOLS":{
"HEADER":"Tools",
"ADD_NEW":"Create a new tool",
"SOFT_LIMIT_WARNING":"Having more than 10 tools may reduce the assistant's reliability in selecting the right tool. Consider removing unused tools for better results.",
"EMPTY_STATE":{
"TITLE":"No custom tools available",
"SUBTITLE":"Create custom tools to connect your assistant with external APIs and services, enabling it to fetch data and perform actions on your behalf.",
"DESCRIPTION":"An agent is a member of your customer support team who can view and respond to user messages. The list below shows all the agents in your account.",
"LEARN_MORE":"Learn about user roles",
"HEADER":"এজেন্ট",
"HEADER_BTN_TXT":"এজেন্ট যোগ করুন",
"LOADING":"এজেন্ট তালিকা আনছে",
"DESCRIPTION":"একজন এজেন্ট হলেন আপনার গ্রাহক সাপোর্ট দলের সদস্য যিনি ব্যবহারকারীর বার্তা দেখতে এবং উত্তর দিতে পারেন। নিচের তালিকায় আপনার অ্যাকাউন্টের সব এজেন্ট দেখানো হয়েছে।",
"LEARN_MORE":"ব্যবহারকারীর ভূমিকা সম্পর্কে জানুন",
"AGENT_TYPES":{
"ADMINISTRATOR":"Administrator",
"AGENT":"Agent"
"ADMINISTRATOR":"প্রশাসক",
"AGENT":"এজেন্ট"
},
"COUNT":"{n} agent | {n} agents",
"COUNT":"{n} এজেন্ট | {n} এজেন্ট",
"LIST":{
"404":"There are no agents associated to this account",
"TITLE":"Manage agents in your team",
"DESC":"You can add/remove agents to/in your team.",
"NAME":"Name",
"EMAIL":"EMAIL",
"STATUS":"Status",
"ACTIONS":"Actions",
"VERIFIED":"Verified",
"VERIFICATION_PENDING":"Verification Pending",
"AVAILABLE_CUSTOM_ROLE":"Available custom role permissions"
"404":"এই অ্যাকাউন্টে কোনো এজেন্ট নেই",
"TITLE":"আপনার দলের এজেন্ট পরিচালনা করুন",
"DESC":"আপনি আপনার দলে এজেন্ট যোগ/অপসারণ করতে পারেন।",
"ERROR_MESSAGE":"যোগাযোগ মুছে ফেলা যায়নি। অনুগ্রহ করে পরে আবার চেষ্টা করুন।."
}
},
"CONTACT_FORM":{
"FORM":{
"SUBMIT":"Submit",
"CANCEL":"Cancel",
"SUBMIT":"জমা দিন",
"CANCEL":"বাতিল করুন",
"AVATAR":{
"LABEL":"Contact Avatar"
"LABEL":"যোগাযোগের অ্যাভাটার"
},
"NAME":{
"PLACEHOLDER":"Enter the full name of the contact",
"LABEL":"Full Name"
"PLACEHOLDER":"যোগাযোগের পূর্ণ নাম লিখুন",
"LABEL":"পূর্ণ নাম"
},
"BIO":{
"PLACEHOLDER":"Enter the bio of the contact",
"LABEL":"Bio"
"PLACEHOLDER":"যোগাযোগের জীবনী লিখুন",
"LABEL":"জীবনবৃত্তান্ত"
},
"EMAIL_ADDRESS":{
"PLACEHOLDER":"Enter the email address of the contact",
"LABEL":"Email Address",
"DUPLICATE":"This email address is in use for another contact.",
"ERROR":"Please enter a valid email address."
"PLACEHOLDER":"যোগাযোগের ইমেইল ঠিকানা লিখুন",
"LABEL":"ইমেইল ঠিকানা",
"DUPLICATE":"এই ইমেল ঠিকানাটি অন্য একটি যোগাযোগের জন্য ব্যবহৃত হচ্ছে।.",
"ERROR":"একটি বৈধ ইমেল ঠিকানা লিখুন।."
},
"PHONE_NUMBER":{
"PLACEHOLDER":"Enter the phone number of the contact",
"LABEL":"Phone Number",
"HELP":"Phone number should be of E.164 format eg: +1415555555 [+][country code][area code][local phone number]. You can select the dial code from the dropdown.",
"ERROR":"Phone number should be either empty or of E.164 format",
"DIAL_CODE_ERROR":"Please select a dial code from the list",
"DUPLICATE":"This phone number is in use for another contact."
"PLACEHOLDER":"যোগাযোগের ফোন নম্বর লিখুন",
"LABEL":"ফোন নম্বর",
"HELP":"ফোন নম্বরটি অবশ্যই E.164 ফরম্যাটে হতে হবে যেমন: +1415555555 [+][দেশের কোড][এরিয়া কোড][স্থানীয় ফোন নম্বর]. আপনি ড্রপডাউন থেকে ডায়াল কোড নির্বাচন করতে পারেন.",
"ERROR":"ফোন নম্বরটি খালি অথবা E.164 ফরম্যাটে হতে হবে",
"DIAL_CODE_ERROR":"অনুগ্রহ করে তালিকা থেকে একটি ডায়াল কোড নির্বাচন করুন",
"DUPLICATE":"এই ফোন নম্বরটি অন্য একটি যোগাযোগের জন্য ব্যবহৃত হচ্ছে।."
},
"LOCATION":{
"PLACEHOLDER":"Enter the location of the contact",
"ERROR":"অ্যাট্রিবিউট মুছে ফেলা যায়নি। অনুগ্রহ করে পরে আবার চেষ্টা করুন"
},
"ATTRIBUTE_SELECT":{
"TITLE":"Add attributes",
"PLACEHOLDER":"Search attributes",
"NO_RESULT":"No attributes found"
"TITLE":"অ্যাট্রিবিউট যোগ করুন",
"PLACEHOLDER":"অ্যাট্রিবিউট অনুসন্ধান করুন",
"NO_RESULT":"কোনো অ্যাট্রিবিউট পাওয়া যায়নি"
},
"ATTRIBUTE_TYPE":{
"LIST":{
"PLACEHOLDER":"Select value",
"SEARCH_INPUT_PLACEHOLDER":"Search value",
"NO_RESULT":"No result found"
"PLACEHOLDER":"মান নির্বাচন করুন",
"SEARCH_INPUT_PLACEHOLDER":"মান অনুসন্ধান করুন",
"NO_RESULT":"কোন ফলাফল পাওয়া যায়নি"
}
}
},
"VALIDATIONS":{
"REQUIRED":"Valid value is required",
"INVALID_URL":"Invalid URL",
"INVALID_INPUT":"Invalid Input"
"REQUIRED":"বৈধ মান আবশ্যক",
"INVALID_URL":"অবৈধ URL",
"INVALID_INPUT":"অবৈধ ইনপুট"
}
},
"MERGE_CONTACTS":{
"TITLE":"Merge contacts",
"DESCRIPTION":"Merge contacts to combine two profiles into one, including all attributes and conversations. In case of conflict, the Primary contact’s attributes will take precedence.",
"TITLE":"যোগাযোগ একত্রিত করুন",
"DESCRIPTION":"দুটি প্রোফাইল একত্রিত করতে কন্টাক্ট মার্জ করুন, এতে সব অ্যাট্রিবিউট ও কথোপকথন একত্রিত হবে। কোনো দ্বন্দ্ব হলে, প্রাইমারি কন্টাক্টের অ্যাট্রিবিউট অগ্রাধিকার পাবে।.",
"PRIMARY":{
"TITLE":"Primary contact",
"HELP_LABEL":"To be deleted"
"TITLE":"প্রাথমিক যোগাযোগ",
"HELP_LABEL":"মুছে ফেলা হবে"
},
"PARENT":{
"TITLE":"Contact to merge",
"PLACEHOLDER":"Search for a contact",
"HELP_LABEL":"To be kept"
"TITLE":"যে কন্টাক্ট মার্জ করা হবে",
"PLACEHOLDER":"একটি যোগাযোগ খুঁজুন",
"HELP_LABEL":"রাখা হবে"
},
"SUMMARY":{
"TITLE":"Summary",
"DELETE_WARNING":"Contact of <strong>{primaryContactName}</strong> will be deleted.",
"ATTRIBUTE_WARNING":"Contact details of <strong>{primaryContactName}</strong> will be copied to <strong>{parentContactName}</strong>."
"TITLE":"সারাংশ",
"DELETE_WARNING":"<strong>{primaryContactName}</strong> এর কন্টাক্ট মুছে ফেলা হবে.",
"ATTRIBUTE_WARNING":"<strong>{primaryContactName}</strong> এর কন্টাক্টের বিস্তারিত <strong>{parentContactName}</strong> এ কপি করা হবে."
},
"SEARCH":{
"ERROR_MESSAGE":"Something went wrong. Please try again later."
},
"FORM":{
"SUBMIT":" Merge contacts",
"CANCEL":"Cancel",
"SUBMIT":" যোগাযোগসমূহ একত্রিত করুন",
"CANCEL":"বাতিল করুন",
"CHILD_CONTACT":{
"ERROR":"Select a child contact to merge"
"ERROR":"একত্রিত করার জন্য একটি চাইল্ড কন্টাক্ট নির্বাচন করুন"
},
"SUCCESS_MESSAGE":"Contact merged successfully",
"ERROR_MESSAGE":"Could not merge contacts, try again!"
"UPDATE_ERROR":"বৈশিষ্ট্য আপডেট করা যায়নি। অনুগ্রহ করে পরে আবার চেষ্টা করুন",
"DELETE_ERROR":"বৈশিষ্ট্য মুছে ফেলা যায়নি। অনুগ্রহ করে পরে আবার চেষ্টা করুন"
}
},
"MERGE":{
"TITLE":"Merge contact",
"DESCRIPTION":"Combine two profiles into one, including all attributes and conversations. In case of conflict, the primary contact’s attributes will take precedence.",
"PRIMARY":"Primary contact",
"PRIMARY_HELP_LABEL":"To be saved",
"PRIMARY_REQUIRED_ERROR":"Please select a contact to merge with before proceeding",
"PARENT":"To be merged",
"PARENT_HELP_LABEL":"To be deleted",
"EMPTY_STATE":"No contacts found",
"PLACEHOLDER":"Search for primary contact",
"SEARCH_PLACEHOLDER":"Search for a contact",
"SEARCH_ERROR_MESSAGE":"Could not search for contacts. Please try again later.",
"SUCCESS_MESSAGE":"Contact merged successfully",
"ERROR_MESSAGE":"Could not merge contacts, try again!",
"IS_SEARCHING":"Searching...",
"TITLE":"কন্টাক্ট একত্রিত করুন",
"DESCRIPTION":"দুটি প্রোফাইলের সব বৈশিষ্ট্য ও কথোপকথন একত্র করে একটি প্রোফাইল বানান. কোনো দ্বন্দ্ব হলে, প্রধান কন্টাক্টের তথ্য আগে ধরা হবে.",
"PRIMARY":"প্রধান কন্টাক্ট",
"PRIMARY_HELP_LABEL":"সংরক্ষণ করা হবে",
"PRIMARY_REQUIRED_ERROR":"অনুগ্রহ করে আগে একত্র করার জন্য একটি কন্টাক্ট বেছে নিন",
"PARENT":"একত্রিত করা হবে",
"PARENT_HELP_LABEL":"মুছে ফেলা হবে",
"EMPTY_STATE":"কোনো কন্টাক্ট পাওয়া যায়নি",
"PLACEHOLDER":"প্রধান কন্টাক্ট খুঁজুন",
"SEARCH_PLACEHOLDER":"একটি কন্টাক্ট খুঁজুন",
"SEARCH_ERROR_MESSAGE":"কন্টাক্ট খোঁজা যায়নি. অনুগ্রহ করে পরে আবার চেষ্টা করুন.",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER":"This Instagram account was migrated to the new Instagram channel inbox. All new messages will show up there. You won’t be able to send messages from this conversation anymore.",
"REPLYING_TO":"You are replying to:",
"REMOVE_SELECTION":"Remove Selection",
"DOWNLOAD":"Download",
"UNKNOWN_FILE_TYPE":"Unknown File",
"SAVE_CONTACT":"Save Contact",
"NO_CONTENT":"No content to display",
"NOT_ASSIGNED_TO_YOU":"এই কথোপকথনটি আপনার কাছে বরাদ্দ নয়। আপনি কি এটি নিজের কাছে বরাদ্দ করতে চান?",
"ASSIGN_TO_ME":"নিজের কাছে বরাদ্দ করুন",
"BOT_HANDOFF_MESSAGE":"আপনি এমন একটি কথোপকথনে উত্তর দিচ্ছেন যা বর্তমানে সহকারী বা বট দ্বারা পরিচালিত হচ্ছে।",
"BOT_HANDOFF_ACTION":"ওপেন করুন এবং নিজের কাছে অ্যাসাইন করুন",
"BOT_HANDOFF_REOPEN_ACTION":"কথোপকথন ওপেন করুন",
"BOT_HANDOFF_SUCCESS":"কথোপকথনটি আপনার কাছে হস্তান্তর করা হয়েছে",
"BOT_HANDOFF_ERROR":"কথোপকথনটি নেওয়া যায়নি। আবার চেষ্টা করুন।",
"TWILIO_WHATSAPP_CAN_REPLY":"আপনি শুধুমাত্র টেমপ্লেট বার্তা ব্যবহার করে এই আলাপে উত্তর দিতে পারবেন কারণ",
"OLD_INSTAGRAM_INBOX_REPLY_BANNER":"এই Instagram অ্যাকাউন্টটি নতুন Instagram চ্যানেল ইনবক্সে স্থানান্তরিত হয়েছে। সব নতুন বার্তা সেখানে দেখা যাবে। আপনি আর এই কথোপকথন থেকে বার্তা পাঠাতে পারবেন না।",
"DESCRIPTION":"View all the conversations from your customers in one single dashboard. You can filter the conversations by the incoming channel, label and status.",
"NEW_LINK":"Click here to create an inbox"
"TITLE":"আপনার সব কথোপকথন এক জায়গায়",
"DESCRIPTION":"একটি ড্যাশবোর্ডে আপনার গ্রাহকদের সব কথোপকথন দেখুন। আপনি চ্যানেল, লেবেল ও স্ট্যাটাস অনুযায়ী ফিল্টার করতে পারবেন।",
"NEW_LINK":"ইনবক্স তৈরি করতে এখানে ক্লিক করুন"
},
"TEAM_MEMBERS":{
"TITLE":"Invite your team members",
"DESCRIPTION":"Since you are getting ready to talk to your customer, bring in your teammates to assist you. You can invite your teammates by adding their email addresses to the agent list.",
"NEW_LINK":"Click here to invite a team member"
"TITLE":"আপনার টিম সদস্যদের আমন্ত্রণ জানান",
"DESCRIPTION":"আপনি যখন গ্রাহকের সাথে কথা বলার জন্য প্রস্তুতি নিচ্ছেন, তখন সহায়তার জন্য আপনার টিমমেটদের যুক্ত করুন। এজেন্ট তালিকায় তাদের ইমেইল ঠিকানা যোগ করে আমন্ত্রণ জানাতে পারেন।",
"NEW_LINK":"টিম সদস্য আমন্ত্রণ জানাতে এখানে ক্লিক করুন"
},
"LABELS":{
"TITLE":"Organize conversations with labels",
"DESCRIPTION":"Labels provide an easier way to categorize your conversation. Create some labels like #support-enquiry, #billing-question etc., so that you can use them in a conversation later.",
"NEW_LINK":"Click here to create tags"
"TITLE":"লেবেল দিয়ে কথোপকথন সাজান",
"DESCRIPTION":"লেবেল ব্যবহার করে সহজেই কথোপকথন শ্রেণিবদ্ধ করুন। যেমন #support-enquiry, #billing-question ইত্যাদি লেবেল তৈরি করুন, পরে কথোপকথনে ব্যবহার করতে পারবেন।",
"NEW_LINK":"ট্যাগ তৈরি করতে এখানে ক্লিক করুন"
},
"CANNED_RESPONSES":{
"TITLE":"Create canned responses",
"DESCRIPTION":"Pre-written quick reply templates help you quickly respond to a conversation. Agents can type the '/' character followed by the shortcode to insert a response.",
"NEW_LINK":"Click here to create a canned response"
"TITLE":"ক্যানড রেসপন্স তৈরি করুন",
"DESCRIPTION":"আগে থেকে লেখা দ্রুত উত্তর টেমপ্লেট দিয়ে সহজেই কথোপকথনে উত্তর দিন। এজেন্টরা '/' চিহ্ন ও শর্টকোড লিখে দ্রুত উত্তর যোগ করতে পারেন।",
"NEW_LINK":"ক্যানড রেসপন্স তৈরি করতে এখানে ক্লিক করুন"
"ENHANCE_SECURITY":"Enhance Your Account Security",
"ENHANCE_SECURITY_DESC":"Two-factor authentication adds an extra layer of security by requiring a verification code from your authenticator app in addition to your password.",
"TITLE":"দ্বি-স্তরীয় প্রমাণীকরণ",
"SUBTITLE":"TOTP-ভিত্তিক প্রমাণীকরণের মাধ্যমে আপনার অ্যাকাউন্টকে অননুমোদিত প্রবেশ থেকে সুরক্ষিত করুন। এটি আপনার অ্যাকাউন্টে অতিরিক্ত নিরাপত্তা যোগ করে.",
"DESCRIPTION":"সময়-ভিত্তিক একবারের পাসওয়ার্ড (TOTP) ব্যবহার করে আপনার অ্যাকাউন্টে অতিরিক্ত নিরাপত্তা যোগ করুন",
"STATUS_TITLE":"প্রমাণীকরণের অবস্থা",
"STATUS_DESCRIPTION":"আপনার দ্বি-স্তরীয় প্রমাণীকরণ সেটিংস এবং ব্যাকআপ পুনরুদ্ধার কোডগুলি পরিচালনা করুন",
"STATUS_ENABLED_DESC":"আপনার অ্যাকাউন্ট অতিরিক্ত নিরাপত্তা স্তর দ্বারা সুরক্ষিত",
"ENABLE_BUTTON":"দ্বি-স্তরীয় প্রমাণীকরণ চালু করুন",
"ENHANCE_SECURITY":"আপনার অ্যাকাউন্টের নিরাপত্তা বৃদ্ধি করুন",
"ENHANCE_SECURITY_DESC":"দ্বি-স্তরীয় প্রমাণীকরণ আপনার নিরাপত্তা আরও বাড়ায়, কারণ এটি আপনার পাসওয়ার্ডের পাশাপাশি আপনার authenticator অ্যাপ থেকে একটি যাচাইকরণ কোড চায়.",
"SETUP":{
"STEP_NUMBER_1":"1",
"STEP_NUMBER_2":"2",
"STEP1_TITLE":"Scan QR Code with Your Authenticator App",
"STEP1_DESCRIPTION":"Use Google Authenticator, Authy, or any TOTP-compatible app",
"LOADING_QR":"Loading...",
"MANUAL_ENTRY":"Can't scan? Enter code manually",
"SECRET_KEY":"Secret Key",
"COPY":"Copy",
"ENTER_CODE":"Enter the 6-digit code from your authenticator app",
"ENTER_CODE_PLACEHOLDER":"000000",
"VERIFY_BUTTON":"Verify & Continue",
"CANCEL":"Cancel",
"ERROR_STARTING":"MFA not enabled. Please contact administrator.",
"INVALID_CODE":"Invalid verification code",
"SECRET_COPIED":"Secret key copied to clipboard",
"SUCCESS":"Two-factor authentication has been enabled successfully"
"STEP_NUMBER_1":"১",
"STEP_NUMBER_2":"২",
"STEP1_TITLE":"আপনার Authenticator অ্যাপ দিয়ে QR কোড স্ক্যান করুন",
"STEP1_DESCRIPTION":"Google Authenticator, Authy, অথবা যেকোনো TOTP-সামঞ্জস্যপূর্ণ অ্যাপ ব্যবহার করুন",
"LOADING_QR":"লোড হচ্ছে...",
"MANUAL_ENTRY":"স্ক্যান করতে পারছেন না? কোডটি ম্যানুয়ালি লিখুন",
"SECRET_KEY":"সিক্রেট কী",
"COPY":"কপি করুন",
"ENTER_CODE":"আপনার অথেন্টিকেটর অ্যাপ থেকে ৬-সংখ্যার কোডটি লিখুন",
"ENTER_CODE_PLACEHOLDER":"০০০০০০",
"VERIFY_BUTTON":"যাচাই করুন ও চালিয়ে যান",
"CANCEL":"বাতিল করুন",
"ERROR_STARTING":"MFA সক্রিয় নয়। অনুগ্রহ করে প্রশাসকের সাথে যোগাযোগ করুন.",
"INVALID_CODE":"ভুল যাচাইকরণ কোড",
"SECRET_COPIED":"সিক্রেট কী ক্লিপবোর্ডে কপি করা হয়েছে",
"SUCCESS":"দ্বৈত-ধাপ যাচাইকরণ সফলভাবে সক্রিয় করা হয়েছে"
},
"BACKUP":{
"TITLE":"Save Your Backup Codes",
"DESCRIPTION":"Keep these codes safe. Each can be used once if you lose access to your authenticator",
"IMPORTANT":"Important:",
"IMPORTANT_NOTE":" Save these codes in a secure location. You won't be able to see them again.",
"DOWNLOAD":"Download",
"COPY_ALL":"Copy All",
"CONFIRM":"I have saved my backup codes in a secure location and understand that I won't be able to see them again",
"COMPLETE_SETUP":"Complete Setup",
"CODES_COPIED":"Backup codes copied to clipboard"
"TITLE":"আপনার ব্যাকআপ কোড সংরক্ষণ করুন",
"DESCRIPTION":"এই কোডগুলো নিরাপদে রাখুন। আপনার অথেন্টিকেটরে প্রবেশ হারালে প্রতিটি কোড একবার করে ব্যবহার করা যাবে",
"IMPORTANT":"গুরুত্বপূর্ণ:",
"IMPORTANT_NOTE":" এই কোডগুলো নিরাপদ স্থানে সংরক্ষণ করুন। আপনি এগুলো আর দেখতে পারবেন না.",
"DOWNLOAD":"ডাউনলোড করুন",
"COPY_ALL":"সব কপি করুন",
"CONFIRM":"আমি আমার ব্যাকআপ কোডগুলো নিরাপদ স্থানে সংরক্ষণ করেছি এবং বুঝতে পেরেছি যে এগুলো আমি আর দেখতে পারব না",
"COMPLETE_SETUP":"সেটআপ সম্পন্ন করুন",
"CODES_COPIED":"ব্যাকআপ কোডগুলো ক্লিপবোর্ডে কপি করা হয়েছে"
},
"MANAGEMENT":{
"BACKUP_CODES":"Backup Codes",
"BACKUP_CODES_DESC":"Generate new codes if you've lost or used your existing ones",
"REGENERATE":"Regenerate Backup Codes",
"DISABLE_MFA":"Disable 2FA",
"DISABLE_MFA_DESC":"Remove two-factor authentication from your account",
"DESCRIPTION":"You'll need to enter your password and a verification code to disable two-factor authentication.",
"PASSWORD":"Password",
"OTP_CODE":"Verification Code",
"OTP_CODE_PLACEHOLDER":"000000",
"CONFIRM":"Disable 2FA",
"CANCEL":"Cancel",
"SUCCESS":"Two-factor authentication has been disabled",
"ERROR":"Failed to disable MFA. Please check your credentials."
"TITLE":"দুই-ধাপ যাচাইকরণ নিষ্ক্রিয় করুন",
"DESCRIPTION":"দুই-ধাপ যাচাইকরণ নিষ্ক্রিয় করতে আপনাকে আপনার পাসওয়ার্ড এবং একটি যাচাইকরণ কোড প্রবেশ করতে হবে.",
"PASSWORD":"পাসওয়ার্ড",
"OTP_CODE":"ভেরিফিকেশন কোড",
"OTP_CODE_PLACEHOLDER":"০০০০০০",
"CONFIRM":"2FA নিষ্ক্রিয় করুন",
"CANCEL":"বাতিল করুন",
"SUCCESS":"টু-ফ্যাক্টর অথেন্টিকেশন নিষ্ক্রিয় করা হয়েছে",
"ERROR":"MFA নিষ্ক্রিয় করতে ব্যর্থ হয়েছে। অনুগ্রহ করে আপনার শংসাপত্র যাচাই করুন."
},
"REGENERATE":{
"TITLE":"Regenerate Backup Codes",
"DESCRIPTION":"This will invalidate your existing backup codes and generate new ones. Enter your verification code to continue.",
"OTP_CODE":"Verification Code",
"OTP_CODE_PLACEHOLDER":"000000",
"CONFIRM":"Generate New Codes",
"CANCEL":"Cancel",
"NEW_CODES_TITLE":"New Backup Codes Generated",
"NEW_CODES_DESC":"Your old backup codes have been invalidated. Save these new codes in a secure location.",
"CODES_IMPORTANT":"Important:",
"CODES_IMPORTANT_NOTE":" Each code can only be used once. Save them before closing this window.",
"DOWNLOAD_CODES":"Download Codes",
"COPY_ALL_CODES":"Copy All Codes",
"CODES_SAVED":"I've Saved My Codes",
"SUCCESS":"New backup codes have been generated",
"ERROR":"Failed to regenerate backup codes"
"TITLE":"ব্যাকআপ কোড পুনরায় তৈরি করুন",
"DESCRIPTION":"এটি আপনার বর্তমান ব্যাকআপ কোডসমূহ বাতিল করবে এবং নতুন কোড তৈরি করবে। চালিয়ে যেতে আপনার যাচাইকরণ কোড দিন.",
"OTP_CODE":"যাচাইকরণ কোড",
"OTP_CODE_PLACEHOLDER":"০০০০০০",
"CONFIRM":"নতুন কোড তৈরি করুন",
"CANCEL":"বাতিল করুন",
"NEW_CODES_TITLE":"নতুন ব্যাকআপ কোড তৈরি হয়েছে",
"NEW_CODES_DESC":"আপনার পুরোনো ব্যাকআপ কোডগুলো বাতিল করা হয়েছে। এই নতুন কোডগুলো নিরাপদ স্থানে সংরক্ষণ করুন.",
"CODES_IMPORTANT":"গুরুত্বপূর্ণ:",
"CODES_IMPORTANT_NOTE":" প্রতিটি কোড শুধুমাত্র একবার ব্যবহার করা যাবে। এই উইন্ডো বন্ধ করার আগে কোডগুলো সংরক্ষণ করুন.",
"DOWNLOAD_CODES":"কোড ডাউনলোড করুন",
"COPY_ALL_CODES":"সব কোড কপি করুন",
"CODES_SAVED":"আমি আমার কোডগুলো সংরক্ষণ করেছি",
"SUCCESS":"নতুন ব্যাকআপ কোড তৈরি হয়েছে",
"ERROR":"ব্যাকআপ কোড পুনরায় তৈরি করতে ব্যর্থ হয়েছে"
}
},
"MFA_VERIFICATION":{
"TITLE":"Two-Factor Authentication",
"DESCRIPTION":"Enter your verification code to continue",
"AUTHENTICATOR_APP":"Authenticator App",
"BACKUP_CODE":"Backup Code",
"ENTER_OTP_CODE":"Enter 6-digit code from your authenticator app",
"ENTER_BACKUP_CODE":"Enter one of your backup codes",
"BACKUP_CODE_PLACEHOLDER":"000000",
"VERIFY_BUTTON":"Verify",
"TRY_ANOTHER_METHOD":"Try another verification method",
"CANCEL_LOGIN":"Cancel and return to login",
"HELP_TEXT":"Having trouble signing in?",
"LEARN_MORE":"Learn more about 2FA",
"TITLE":"দ্বি-স্তরীয় প্রমাণীকরণ",
"DESCRIPTION":"চালিয়ে যেতে আপনার যাচাইকরণ কোড দিন",
"AUTHENTICATOR_APP":"অথেন্টিকেটর অ্যাপ",
"BACKUP_CODE":"ব্যাকআপ কোড",
"ENTER_OTP_CODE":"আপনার authenticator অ্যাপ থেকে ৬-সংখ্যার কোডটি লিখুন",
"ENTER_BACKUP_CODE":"আপনার ব্যাকআপ কোডগুলোর মধ্যে একটি লিখুন",
"BACKUP_CODE_PLACEHOLDER":"০০০০০০",
"VERIFY_BUTTON":"যাচাই করুন",
"TRY_ANOTHER_METHOD":"অন্য একটি যাচাইকরণ পদ্ধতি চেষ্টা করুন",
"CANCEL_LOGIN":"বাতিল করুন এবং লগইন পৃষ্ঠায় ফিরে যান",
"HELP_TEXT":"সাইন ইন করতে সমস্যা হচ্ছে?",
"LEARN_MORE":"2FA সম্পর্কে আরও জানুন",
"HELP_MODAL":{
"TITLE":"Two-Factor Authentication Help",
"AUTHENTICATOR_TITLE":"Using an Authenticator App",
"AUTHENTICATOR_DESC":"Open your authenticator app (Google Authenticator, Authy, etc.) and enter the 6-digit code shown for your account.",
"BACKUP_TITLE":"Using a Backup Code",
"BACKUP_DESC":"If you don't have access to your authenticator app, you can use one of the backup codes you saved when setting up 2FA. Each code can only be used once.",
"CONTACT_TITLE":"Need More Help?",
"CONTACT_DESC_CLOUD":"If you've lost access to both your authenticator app and backup codes, please reach out to Chatwoot support for assistance.",
"CONTACT_DESC_SELF_HOSTED":"If you've lost access to both your authenticator app and backup codes, please contact your administrator for assistance."
"TITLE":"টু-ফ্যাক্টর অথেন্টিকেশন সহায়তা",
"AUTHENTICATOR_TITLE":"অথেন্টিকেটর অ্যাপ ব্যবহার করা হচ্ছে",
"AUTHENTICATOR_DESC":"আপনার authenticator অ্যাপ (Google Authenticator, Authy, ইত্যাদি) খুলুন এবং আপনার অ্যাকাউন্টের জন্য দেখানো ৬-সংখ্যার কোডটি লিখুন.",
"BACKUP_TITLE":"ব্যাকআপ কোড ব্যবহার করুন",
"BACKUP_DESC":"যদি আপনার authenticator অ্যাপে প্রবেশাধিকার না থাকে, তাহলে 2FA সেটআপ করার সময় সংরক্ষণ করা ব্যাকআপ কোডগুলোর যেকোনো একটি ব্যবহার করতে পারেন। প্রতিটি কোড কেবল একবারই ব্যবহার করা যাবে.",
"CONTACT_TITLE":"আরও সাহায্য দরকার?",
"CONTACT_DESC_CLOUD":"আপনি যদি আপনার authenticator অ্যাপ এবং ব্যাকআপ কোড দুটোই হারিয়ে ফেলেন, তাহলে সহায়তার জন্য অনুগ্রহ করে Chatwoot সাপোর্ট টিমের সাথে যোগাযোগ করুন.",
"CONTACT_DESC_SELF_HOSTED":"আপনি যদি আপনার authenticator অ্যাপ এবং ব্যাকআপ কোড দুটোই হারিয়ে ফেলেন, সহায়তার জন্য অনুগ্রহ করে আপনার অ্যাডমিনিস্ট্রেটরের সাথে যোগাযোগ করুন."
"DATA_FETCHING_FAILED":"তথ্য আনতে ব্যর্থ হয়েছে, পরে আবার চেষ্টা করুন।",
"SUMMARY_FETCHING_FAILED":"সারাংশ আনতে ব্যর্থ হয়েছে, পরে আবার চেষ্টা করুন।",
"METRICS":{
"CONVERSATIONS":{
"NAME":"Conversations",
"DESC":"( Total )"
"NAME":"কনভার্সেশন",
"DESC":"( মোট )"
},
"INCOMING_MESSAGES":{
"NAME":"Messages received",
"DESC":"( Total )"
"NAME":"প্রাপ্ত বার্তা",
"DESC":"( মোট )"
},
"OUTGOING_MESSAGES":{
"NAME":"Messages sent",
"DESC":"( Total )"
"NAME":"প্রেরিত বার্তা",
"DESC":"( মোট )"
},
"FIRST_RESPONSE_TIME":{
"NAME":"First Response Time",
"DESC":"( Avg )",
"INFO_TEXT":"Total number of conversations used for computation:",
"NAME":"প্রথম সাড়া দেওয়ার সময়",
"DESC":"( গড় )",
"INFO_TEXT":"গণনার জন্য ব্যবহৃত মোট কথোপকথনের সংখ্যা:",
"TOOLTIP_TEXT":"First Response Time is {metricValue} (based on {conversationCount} conversations)"
},
"RESOLUTION_TIME":{
"NAME":"Resolution Time",
"DESC":"( Avg )",
"INFO_TEXT":"Total number of conversations used for computation:",
"NAME":"সমাধানের সময়",
"DESC":"( গড় )",
"INFO_TEXT":"গণনার জন্য ব্যবহৃত মোট কথোপকথনের সংখ্যা:",
"TOOLTIP_TEXT":"Resolution Time is {metricValue} (based on {conversationCount} conversations)"
},
"RESOLUTION_COUNT":{
"NAME":"Resolution Count",
"DESC":"( Total )"
"NAME":"সমাধানের সংখ্যা",
"DESC":"( মোট )"
},
"BOT_RESOLUTION_COUNT":{
"NAME":"Resolution Count",
"DESC":"( Total )"
"NAME":"সমাধানের সংখ্যা",
"DESC":"( মোট )"
},
"BOT_HANDOFF_COUNT":{
"NAME":"Handoff Count",
"DESC":"( Total )"
"NAME":"হস্তান্তরের সংখ্যা",
"DESC":"( মোট )"
},
"REPLY_TIME":{
"NAME":"Customer waiting time",
"NAME":"গ্রাহকের অপেক্ষার সময়",
"TOOLTIP_TEXT":"Waiting time is {metricValue} (based on {conversationCount} replies)",
"DESC":""
}
},
"DATE_RANGE_OPTIONS":{
"LAST_7_DAYS":"Last 7 days",
"LAST_14_DAYS":"Last 14 days",
"LAST_30_DAYS":"Last 30 days",
"THIS_MONTH":"This month",
"LAST_MONTH":"Last month",
"LAST_3_MONTHS":"Last 3 months",
"LAST_6_MONTHS":"Last 6 months",
"LAST_YEAR":"Last year",
"CUSTOM_DATE_RANGE":"Custom date range"
"LAST_7_DAYS":"গত ৭ দিন",
"LAST_14_DAYS":"গত ১৪ দিন",
"LAST_30_DAYS":"গত ৩০ দিন",
"THIS_MONTH":"এই মাস",
"LAST_MONTH":"গত মাস",
"LAST_3_MONTHS":"গত ৩ মাস",
"LAST_6_MONTHS":"গত ৬ মাস",
"LAST_YEAR":"গত বছর",
"CUSTOM_DATE_RANGE":"কাস্টম তারিখ পরিসর"
},
"CUSTOM_DATE_RANGE":{
"CONFIRM":"Apply",
"PLACEHOLDER":"Select date range"
"CONFIRM":"প্রয়োগ করুন",
"PLACEHOLDER":"তারিখ নির্বাচন করুন"
},
"GROUP_BY_FILTER_DROPDOWN_LABEL":"Group By",
"DURATION_FILTER_LABEL":"Duration",
"GROUP_BY_FILTER_DROPDOWN_LABEL":"গ্রুপ করুন",
"DURATION_FILTER_LABEL":"সময়কাল",
"GROUPING_OPTIONS":{
"DAY":"Day",
"WEEK":"Week",
"MONTH":"Month",
"YEAR":"Year"
"DAY":"দিন",
"WEEK":"সপ্তাহ",
"MONTH":"মাস",
"YEAR":"বছর"
},
"GROUP_BY_DAY_OPTIONS":[
{
"id":1,
"groupBy":"Day"
"groupBy":"দিন"
}
],
"GROUP_BY_WEEK_OPTIONS":[
{
"id":1,
"groupBy":"Day"
"groupBy":"দিন"
},
{
"id":2,
"groupBy":"Week"
"groupBy":"সপ্তাহ"
}
],
"GROUP_BY_MONTH_OPTIONS":[
{
"id":1,
"groupBy":"Day"
"groupBy":"দিন"
},
{
"id":2,
"groupBy":"Week"
"groupBy":"সপ্তাহ"
},
{
"id":3,
"groupBy":"Month"
"groupBy":"মাস"
}
],
"GROUP_BY_YEAR_OPTIONS":[
{
"id":2,
"groupBy":"Week"
"groupBy":"সপ্তাহ"
},
{
"id":3,
"groupBy":"Month"
"groupBy":"মাস"
},
{
"id":4,
"groupBy":"Year"
"groupBy":"বছর"
}
],
"BUSINESS_HOURS":"Business Hours",
"BUSINESS_HOURS":"ব্যবসায়িক সময়",
"FILTER_ACTIONS":{
"CLEAR_FILTER":"Clear filter",
"EMPTY_LIST":"No results found"
"CLEAR_FILTER":"ফিল্টার পরিষ্কার করুন",
"EMPTY_LIST":"কোনও ফলাফল পাওয়া যায়নি"
},
"PAGINATION":{
"RESULTS":"Showing {start} to {end} of {total} results",
@@ -127,524 +127,524 @@
}
},
"AGENT_REPORTS":{
"HEADER":"Agents Overview",
"DESCRIPTION":"Easily track agent performance with key metrics such as conversations, response times, resolution times, and resolved cases. Click an agent's name to learn more.",
"LOADING_CHART":"Loading chart data...",
"NO_ENOUGH_DATA":"We've not received enough data points to generate report, Please try again later.",
"DESCRIPTION":"কথোপকথন, সাড়া দেওয়ার সময়, সমাধানের সময় ও সমাধানকৃত কেসসহ গুরুত্বপূর্ণ মেট্রিক্সে সহজেই এজেন্ট পারফরম্যান্স ট্র্যাক করুন। আরও জানতে এজেন্টের নাম ক্লিক করুন।",
"LOADING_CHART":"চার্ট ডেটা লোড হচ্ছে...",
"NO_ENOUGH_DATA":"রিপোর্ট তৈরি করার জন্য যথেষ্ট ডেটা নেই, পরে আবার চেষ্টা করুন।",
"INFO_TEXT":"Total number of conversations used for computation:",
"NAME":"প্রথম সাড়া দেওয়ার সময়",
"DESC":"( গড় )",
"INFO_TEXT":"গণনার জন্য ব্যবহৃত মোট কথোপকথনের সংখ্যা:",
"TOOLTIP_TEXT":"First Response Time is {metricValue} (based on {conversationCount} conversations)"
},
"RESOLUTION_TIME":{
"NAME":"Resolution Time",
"DESC":"( Avg )",
"INFO_TEXT":"Total number of conversations used for computation:",
"NAME":"সমাধান সময়",
"DESC":"( গড় )",
"INFO_TEXT":"গণনার জন্য ব্যবহৃত মোট কথোপকথনের সংখ্যা:",
"TOOLTIP_TEXT":"Resolution Time is {metricValue} (based on {conversationCount} conversations)"
},
"RESOLUTION_COUNT":{
"NAME":"Resolution Count",
"DESC":"( Total )"
"NAME":"সমাধান সংখ্যা",
"DESC":"( মোট )"
}
},
"DATE_RANGE":[
{
"id":0,
"name":"Last 7 days"
"name":"গত ৭ দিন"
},
{
"id":1,
"name":"Last 30 days"
"name":"গত ৩০ দিন"
},
{
"id":2,
"name":"Last 3 months"
"name":"গত ৩ মাস"
},
{
"id":3,
"name":"Last 6 months"
"name":"গত ৬ মাস"
},
{
"id":4,
"name":"Last year"
"name":"গত বছর"
},
{
"id":5,
"name":"Custom date range"
"name":"কাস্টম তারিখ পরিসর"
}
],
"CUSTOM_DATE_RANGE":{
"CONFIRM":"Apply",
"PLACEHOLDER":"Select date range"
"CONFIRM":"প্রয়োগ করুন",
"PLACEHOLDER":"তারিখ পরিসর নির্বাচন করুন"
}
},
"LABEL_REPORTS":{
"HEADER":"Labels Overview",
"DESCRIPTION":"Track label performance with key metrics including conversations, response times, resolution times, and resolved cases. Click a label name for detailed insights.",
"LOADING_CHART":"Loading chart data...",
"NO_ENOUGH_DATA":"We've not received enough data points to generate report, Please try again later.",
"DESCRIPTION":"কথোপকথন, সাড়া দেওয়ার সময়, সমাধানের সময় ও সমাধানকৃত কেসসহ মূল পরিসংখ্যান দিয়ে লেবেলের কার্যকারিতা ট্র্যাক করুন। বিস্তারিত জানতে লেবেলের নাম ক্লিক করুন।",
"LOADING_CHART":"চার্ট ডেটা লোড হচ্ছে...",
"NO_ENOUGH_DATA":"রিপোর্ট তৈরি করার জন্য যথেষ্ট ডেটা নেই, পরে আবার চেষ্টা করুন।",
"INFO_TEXT":"Total number of conversations used for computation:",
"NAME":"প্রথম সাড়া সময়",
"DESC":"( গড় )",
"INFO_TEXT":"গণনার জন্য ব্যবহৃত মোট কথোপকথনের সংখ্যা:",
"TOOLTIP_TEXT":"First Response Time is {metricValue} (based on {conversationCount} conversations)"
},
"RESOLUTION_TIME":{
"NAME":"Resolution Time",
"DESC":"( Avg )",
"INFO_TEXT":"Total number of conversations used for computation:",
"NAME":"সমাধানের সময়",
"DESC":"( গড় )",
"INFO_TEXT":"গণনার জন্য ব্যবহৃত মোট কথোপকথনের সংখ্যা:",
"TOOLTIP_TEXT":"Resolution Time is {metricValue} (based on {conversationCount} conversations)"
},
"RESOLUTION_COUNT":{
"NAME":"Resolution Count",
"DESC":"( Total )"
"NAME":"সমাধান সংখ্যা",
"DESC":"( মোট )"
}
},
"DATE_RANGE":[
{
"id":0,
"name":"Last 7 days"
"name":"গত ৭ দিন"
},
{
"id":1,
"name":"Last 30 days"
"name":"গত ৩০ দিন"
},
{
"id":2,
"name":"Last 3 months"
"name":"গত ৩ মাস"
},
{
"id":3,
"name":"Last 6 months"
"name":"গত ৬ মাস"
},
{
"id":4,
"name":"Last year"
"name":"গত বছর"
},
{
"id":5,
"name":"Custom date range"
"name":"কাস্টম তারিখ পরিসর"
}
],
"CUSTOM_DATE_RANGE":{
"CONFIRM":"Apply",
"PLACEHOLDER":"Select date range"
"CONFIRM":"প্রয়োগ করুন",
"PLACEHOLDER":"তারিখের পরিসর নির্বাচন করুন"
}
},
"INBOX_REPORTS":{
"HEADER":"Inbox Overview",
"DESCRIPTION":"Quickly view your inbox performance with key metrics like conversations, response times, resolution times, and resolved cases—all in one place. Click an inbox name for more details.",
"LOADING_CHART":"Loading chart data...",
"NO_ENOUGH_DATA":"We've not received enough data points to generate report, Please try again later.",
"DESCRIPTION":"কথোপকথন, সাড়া দেওয়ার সময়, সমাধানের সময় ও সমাধানকৃত কেসসহ গুরুত্বপূর্ণ মেট্রিক্সে আপনার ইনবক্সের পারফরম্যান্স এক জায়গায় দ্রুত দেখুন। আরও জানতে ইনবক্সের নাম ক্লিক করুন।",
"LOADING_CHART":"চার্ট ডেটা লোড হচ্ছে...",
"NO_ENOUGH_DATA":"রিপোর্ট তৈরির জন্য যথেষ্ট ডেটা নেই, পরে আবার চেষ্টা করুন।",
"INFO_TEXT":"Total number of conversations used for computation:",
"NAME":"প্রথম সাড়া সময়",
"DESC":"(গড়)",
"INFO_TEXT":"গণনার জন্য ব্যবহৃত মোট কথোপকথনের সংখ্যা:",
"TOOLTIP_TEXT":"First Response Time is {metricValue} (based on {conversationCount} conversations)"
},
"RESOLUTION_TIME":{
"NAME":"Resolution Time",
"DESC":"( Avg )",
"INFO_TEXT":"Total number of conversations used for computation:",
"NAME":"সমাধানের সময়",
"DESC":"(গড়)",
"INFO_TEXT":"গণনার জন্য ব্যবহৃত মোট কথোপকথনের সংখ্যা:",
"TOOLTIP_TEXT":"Resolution Time is {metricValue} (based on {conversationCount} conversations)"
},
"RESOLUTION_COUNT":{
"NAME":"Resolution Count",
"DESC":"( Total )"
"NAME":"সমাধানের সংখ্যা",
"DESC":"(মোট)"
}
},
"DATE_RANGE":[
{
"id":0,
"name":"Last 7 days"
"name":"গত ৭ দিন"
},
{
"id":1,
"name":"Last 30 days"
"name":"গত ৩০ দিন"
},
{
"id":2,
"name":"Last 3 months"
"name":"গত ৩ মাস"
},
{
"id":3,
"name":"Last 6 months"
"name":"গত ৬ মাস"
},
{
"id":4,
"name":"Last year"
"name":"গত বছর"
},
{
"id":5,
"name":"Custom date range"
"name":"কাস্টম তারিখ পরিসর"
}
],
"CUSTOM_DATE_RANGE":{
"CONFIRM":"Apply",
"PLACEHOLDER":"Select date range"
"CONFIRM":"প্রয়োগ করুন",
"PLACEHOLDER":"তারিখ পরিসর নির্বাচন করুন"
}
},
"TEAM_REPORTS":{
"HEADER":"Team Overview",
"DESCRIPTION":"Get a snapshot of your team's performance with essential metrics, including conversations, response times, resolution times, and resolved cases. Click a team name for more details.",
"LOADING_CHART":"Loading chart data...",
"NO_ENOUGH_DATA":"We've not received enough data points to generate report, Please try again later.",
"DOWNLOAD_TEAM_REPORTS":"Download team reports",
"FILTER_DROPDOWN_LABEL":"Select Team",
"HEADER":"টিমের সারসংক্ষেপ",
"DESCRIPTION":"কথোপকথন, সাড়া দেওয়ার সময়, সমাধানের সময় ও সমাধানকৃত কেসসহ গুরুত্বপূর্ণ মেট্রিক্সে আপনার টিমের পারফরম্যান্সের সারসংক্ষেপ দেখুন। আরও জানতে টিমের নাম ক্লিক করুন।",
"LOADING_CHART":"চার্ট ডেটা লোড হচ্ছে...",
"NO_ENOUGH_DATA":"রিপোর্ট তৈরির জন্য যথেষ্ট ডেটা নেই, পরে আবার চেষ্টা করুন।",
"TOOLTIP":"Total number of positive responses / Total number of responses * 100"
"LABEL":"সন্তুষ্টি স্কোর",
"TOOLTIP":"মোট ইতিবাচক প্রতিক্রিয়া / মোট প্রতিক্রিয়া * ১০০"
},
"RESPONSE_RATE":{
"LABEL":"Response rate",
"TOOLTIP":"Total number of responses / Total number of CSAT survey messages sent * 100"
"LABEL":"প্রতিক্রিয়া হার",
"TOOLTIP":"মোট প্রতিক্রিয়া / মোট CSAT জরিপ বার্তা * ১০০"
},
"RATING_DISTRIBUTION":"Rating distribution"
"RATING_DISTRIBUTION":"রেটিং বণ্টন"
},
"REVIEW_NOTES":{
"TITLE":"Review notes",
"PLACEHOLDER":"Add review notes about this rating...",
"SAVE":"Save",
"CANCEL":"Cancel",
"SAVING":"Saving...",
"SAVED":"Notes saved successfully",
"SAVE_ERROR":"Failed to save notes",
"TITLE":"নোটসমূহ পর্যালোচনা করুন",
"PLACEHOLDER":"এই রেটিং সম্পর্কে পর্যালোচনার নোট যোগ করুন...",
"SAVE":"সংরক্ষণ করুন",
"CANCEL":"বাতিল করুন",
"SAVING":"সংরক্ষণ করা হচ্ছে...",
"SAVED":"নোট সফলভাবে সংরক্ষিত হয়েছে",
"SAVE_ERROR":"নোট সংরক্ষণে ব্যর্থ হয়েছে",
"UPDATED_BY":"Updated by {name} {time}",
"UPDATED_BY_LABEL":"Updated by",
"UPDATED_BY_LABEL":"আপডেট করেছেন",
"PAYWALL":{
"TITLE":"Upgrade to add review notes",
"AVAILABLE_ON":"The review notes feature is only available in the Business and Enterprise plans.",
"UPGRADE_PROMPT":"Add internal context to every CSAT response with review notes. Capture what really happened, spot patterns faster, and make better decisions from your feedback.",
"UPGRADE_NOW":"Upgrade now",
"CANCEL_ANYTIME":"You can change or cancel your plan anytime"
"TITLE":"পর্যালোচনার নোট যোগ করতে আপগ্রেড করুন",
"AVAILABLE_ON":"পর্যালোচনার নোট ফিচারটি শুধুমাত্র Business এবং Enterprise প্ল্যানে উপলব্ধ।",
"UPGRADE_PROMPT":"প্রতি CSAT প্রতিক্রিয়ায় রিভিউ নোট যোগ করে অভ্যন্তরীণ তথ্য যুক্ত করুন। কী ঘটেছিল তা ধরুন, দ্রুত প্যাটার্ন চিনুন এবং আপনার ফিডব্যাক থেকে আরও ভালো সিদ্ধান্ত নিন।",
"UPGRADE_NOW":"এখনই আপগ্রেড করুন",
"CANCEL_ANYTIME":"আপনি যেকোনো সময় আপনার প্ল্যান পরিবর্তন বা বাতিল করতে পারেন"
}
}
},
"BOT_REPORTS":{
"HEADER":"Bot Reports",
"HEADER":"বট রিপোর্ট",
"METRIC":{
"TOTAL_CONVERSATIONS":{
"LABEL":"No. of Conversations",
"TOOLTIP":"Total number of conversations handled by the bot"
"LABEL":"কথোপকথনের সংখ্যা",
"TOOLTIP":"বট দ্বারা পরিচালিত মোট কথোপকথন"
},
"TOTAL_RESPONSES":{
"LABEL":"Total Responses",
"TOOLTIP":"Total number of responses sent by the bot"
"LABEL":"মোট উত্তর",
"TOOLTIP":"বট দ্বারা পাঠানো মোট উত্তর"
},
"RESOLUTION_RATE":{
"LABEL":"Resolution Rate",
"TOOLTIP":"Total number of conversations resolved by the bot / Total number of conversations handled by the bot * 100"
"LABEL":"সমাধান হার",
"TOOLTIP":"বট দ্বারা সমাধানকৃত মোট কথোপকথন / বট দ্বারা পরিচালিত মোট কথোপকথন * ১০০"
},
"HANDOFF_RATE":{
"LABEL":"Handoff Rate",
"TOOLTIP":"Total number of conversations handed off to agents / Total number of conversations handled by the bot * 100"
"LABEL":"হ্যান্ডঅফ হার",
"TOOLTIP":"এজেন্টদের কাছে হ্যান্ডঅফ করা মোট কথোপকথন / বট দ্বারা পরিচালিত মোট কথোপকথন * ১০০"
"TOOLTIP":"Percentage of SLAs created were completed successfully"
"LABEL":"হিট হার",
"TOOLTIP":"তৈরি হওয়া SLA-র শতকরা কত সফলভাবে সম্পন্ন হয়েছে"
},
"NO_OF_MISSES":{
"LABEL":"Number of Misses",
"TOOLTIP":"Total SLA misses in a certain period"
"LABEL":"মিসের সংখ্যা",
"TOOLTIP":"নির্দিষ্ট সময়ে মোট SLA মিস"
},
"NO_OF_CONVERSATIONS":{
"LABEL":"Number of Conversations",
"TOOLTIP":"Total number of conversations with SLA"
"LABEL":"কথোপকথনের সংখ্যা",
"TOOLTIP":"SLA সহ মোট কথোপকথনের সংখ্যা"
}
},
"TABLE":{
"HEADER":{
"POLICY":"Policy",
"CONVERSATION":"Conversation",
"AGENT":"Agent"
"POLICY":"নীতি",
"CONVERSATION":"কথোপকথন",
"AGENT":"এজেন্ট"
},
"VIEW_DETAILS":"View Details"
"VIEW_DETAILS":"বিস্তারিত দেখুন"
}
},
"SUMMARY_REPORTS":{
"INBOX":"Inbox",
"AGENT":"Agent",
"TEAM":"Team",
"LABEL":"Label",
"AVG_RESOLUTION_TIME":"Avg. Resolution Time",
"AVG_FIRST_RESPONSE_TIME":"Avg. First Response Time",
"AVG_REPLY_TIME":"Avg. Customer Waiting Time",
"RESOLUTION_COUNT":"Resolution Count",
"CONVERSATIONS":"No. of conversations"
"INBOX":"ইনবক্স",
"AGENT":"এজেন্ট",
"TEAM":"টিম",
"LABEL":"লেবেল",
"AVG_RESOLUTION_TIME":"গড় সমাধান সময়",
"AVG_FIRST_RESPONSE_TIME":"গড় প্রথম সাড়া সময়",
"AVG_REPLY_TIME":"গড় গ্রাহক অপেক্ষার সময়",
"RESOLUTION_COUNT":"সমাধান সংখ্যা",
"CONVERSATIONS":"কথোপকথনের সংখ্যা"
}
}
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.