Commit Graph
40 Commits
Author SHA1 Message Date
8a3b129292 fix: Disable re-oauth flow for manual whatsapp (#13599)
Restrict the WhatsApp reauthorization flag to channels whose
provider_config source is 'embedded_signup'. Previously the view exposed
resource.channel.reauthorization_required? for all WhatsApp channels;
now it only returns true when (provider_config || {}).to_h['source'] ==
'embedded_signup' && resource.channel.reauthorization_required?. This
prevents showing reauthorization prompts for manual/API-key flows
(non-OAuth) and safely handles nil provider_config.

# 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  #13553 

## 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?

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
- [ ] Any dependent changes have been merged and published in downstream
modules

---------

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com>
2026-06-09 11:12:03 +04:00
Shivam MishraandGitHub 73ba0b26e5 fix(instagram): process webhook events after mutex retry exhaustion (#14647)
Instagram webhooks use a Redis mutex to protect the first-message path
for a contact/account pair. When multiple webhook events for the same
Instagram contact arrive at nearly the same time, they can all enter the
“find or create” flow together.

There are two pieces involved:

`ContactInbox` maps the external Instagram scoped user id to a Chatwoot
contact inside an inbox. That side already has a unique `(inbox_id,
source_id)` index and retry handling, so duplicate contact-inbox
creation is mostly protected at the database layer.

`Conversation` creation is more fragile. The message builder looks for
an existing active conversation for the contact/inbox and creates one
when none is found. If two first-message jobs run concurrently, both can
see “no active conversation yet” and both can create a conversation. The
mutex exists to bump those jobs apart long enough for the first one to
create the conversation, so the next one appends to it instead of
creating a duplicate.

In production, the old behavior could turn that race dampener into a
drop path. Once `Webhooks::InstagramEventsJob` exhausted its lock
retries, ActiveJob stopped retrying with
`MutexApplicationJob::LockAcquisitionError`. Since ActiveJob retries are
not FIFO, later jobs could still win the lock while older jobs kept
getting deferred until exhaustion.

## Mutex Application Job Changes

This adds `retry_on_lock_conflict` as a small wrapper around the
existing `retry_on LockAcquisitionError` pattern.

The new API keeps the default behavior intact, but lets jobs explicitly
define what should happen after lock retry exhaustion:

```ruby
retry_on_lock_conflict wait: 1.second,
                       attempts: 3,
                       on_exhaustion: :process_without_lock
```

The fallback receives the original job arguments, so job classes do not
need to reach into ActiveJob internals like `arguments.first`.

## Instagram Fallback

Instagram now processes the webhook payload even after the mutex retry
window is exhausted.

This matches what the mutex was meant to do in the first place: bump
concurrent events apart long enough to avoid duplicate conversation
creation, not permanently block or drop webhook events. If a job cannot
acquire the lock after a few retries, it continues through the normal
Instagram processing path without the mutex.

## Retry And Lock Tuning

The Instagram lock retry window now uses deterministic backoff:

```ruby
retry_on_lock_conflict wait: ->(executions) { executions.seconds },
                       attempts: 3,
                       on_exhaustion: :process_without_lock
```

The lock TTL is also set explicitly:

```ruby
with_lock(key, 3.seconds)
```

This matters because Rails treats `attempts` as total executions, not
retries after the first execution. With a fixed `wait: 1.second` and
`attempts: 3`, the exhaustion handler can run roughly two seconds after
the first lock conflict. That is earlier than a 3-second lock TTL, so
the fallback could process without the lock while the original mutex is
still valid. That would reopen the duplicate-conversation race the lock
is meant to dampen.

Using a proc wait makes the timing predictable and avoids Rails jitter
for this retry path. The first conflict waits about 1 second, the second
waits about 2 seconds, and the final attempt happens around the 3-second
mark. That lines the retry window up with the Redis lock TTL before
falling back to `process_without_lock`.

The protected race window is intentionally small. `ContactInbox`
creation already has database uniqueness protection, while conversation
creation is the softer `find active conversation || create` path. We
only need to give the first job enough time to create the conversation
state that later jobs should reuse.

The mutex still does not preserve message order, and ActiveJob retries
are not FIFO. A longer retry window would mostly add latency for hot
Instagram contacts without making ordering more correct. After the lock
TTL has elapsed, processing without the lock is the better tradeoff than
dead-lettering customer messages.
2026-06-05 14:06:45 +05:30
40deaef458 feat: Store WhatsApp BSUID identifiers from inbound webhooks (#14436)
Adds storage support for WhatsApp business-scoped user identifiers
received from Meta Cloud API and Twilio WhatsApp webhooks. The change
keeps existing phone-based behavior intact, stores BSUID and parent
BSUID values as additional `contact_inboxes.source_id` rows for the same
contact, and allows BSUID-only inbound messages to create contacts,
conversations, and messages without requiring a phone number.

Related: https://github.com/chatwoot/chatwoot/issues/13837

**What changed**
- Extended WhatsApp source ID validation to accept regular BSUID and
parent BSUID formats.
- For Meta Cloud API, stores phone, `user_id`, and `parent_user_id`
identifiers as contact inbox source IDs when they are present.
- For Twilio WhatsApp, stores phone, `ExternalUserId`, and
`ParentExternalUserId` identifiers as contact inbox source IDs while
preserving the existing `whatsapp:` Twilio source ID shape.
- Supports BSUID-only inbound messages by creating a contact, contact
inbox, conversation, and message even when the phone number is missing.
- Links phone-first and later BSUID-only messages to the same contact
when the first payload contains both phone and BSUID.
- Stores WhatsApp usernames in contact `additional_attributes`, matching
existing social channel patterns.
- Keeps existing phone-based outbound and new-conversation behavior
unchanged for this milestone.

**How to test**
1. Send a Meta Cloud webhook payload with both `wa_id` and `user_id`.
2. Verify Chatwoot creates or finds the phone `contact_inbox` and also
creates a BSUID `contact_inbox` for the same contact.
3. Send a later Meta Cloud payload for the same user with only `user_id`
/ `from_user_id`.
4. Verify Chatwoot finds the BSUID `contact_inbox` and creates the
inbound message without requiring a phone number.
5. Send a Twilio WhatsApp webhook with `From: whatsapp:+E164`,
`ExternalUserId`, and optionally `ParentExternalUserId`.
6. Verify Chatwoot stores the Twilio phone and BSUID identifiers as
`whatsapp:`-prefixed source IDs for the same contact.
7. Send a Twilio WhatsApp webhook where `From` is `whatsapp:<BSUID>` and
there is no phone number.
8. Verify Chatwoot creates the contact, contact inbox, conversation, and
message without a phone number.

---------

Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com>
2026-05-20 13:36:43 +04:00
de696a55cb feat(voice): add WhatsApp inbound call webhook pipeline [3] (#14315)
Adds the server-side flow that turns Meta WhatsApp Cloud Calling
webhooks into Chatwoot Calls, conversations, voice_call message bubbles,
and ActionCable broadcasts. Stacked on top of #14312 (PR-2 — provider
methods); intentionally does not include the HTTP controller, routes, or
frontend (those land in PR-4 and PR-9).

## Closes
- Part of the WhatsApp Cloud Calling rollout. Linear: TBD

## What changed

**Webhook routing**
- `app/jobs/webhooks/whatsapp_events_job.rb` — append
`prepend_mod_with('Webhooks::WhatsappEventsJob')` so EE can extend it
without forking.
- `enterprise/app/jobs/enterprise/webhooks/whatsapp_events_job.rb` (new)
— overlay that prepends `handle_message_events` to intercept `field:
'calls'` payloads (route to `Whatsapp::IncomingCallService`) and
`interactive.call_permission_reply` messages (route to
`Whatsapp::CallPermissionReplyService`); falls through with `super` for
regular messages.

**Services**
- `enterprise/app/services/whatsapp/incoming_call_service.rb` (new) —
gated on `provider_config['calling_enabled']`; processes `connect`
(creates inbound call via `Voice::InboundCallBuilder` or transitions an
existing outbound call to `in_progress`) and `terminate` events; updates
conversation `additional_attributes` and broadcasts
`voice_call.incoming`/`voice_call.outbound_connected`/`voice_call.ended`.
- `enterprise/app/services/whatsapp/call_permission_reply_service.rb`
(new) — handles WhatsApp interactive `call_permission_reply` replies;
clears the conversation's `call_permission_requested_at` flag and
broadcasts `voice_call.permission_granted` so the agent UI can re-enable
the call button.

**Builder/model adjustments**
- `enterprise/app/services/voice/inbound_call_builder.rb` —
provider-agnostic; accepts `provider:` and `extra_meta:` kwargs, drops
`account:` (now derived from `inbox.account` to keep the param count
under rubocop's ceiling without disabling cops), uses digits-only
`source_id` for WhatsApp ContactInbox (validation requires
`^\d{1,15}\z`), skips Twilio-only `conference_sid` for non-Twilio
providers.
- `enterprise/app/services/voice/call_message_builder.rb` — adds
`create!`/`update_status!` API and `CALL_TO_VOICE_STATUS` map; uses
direct `Message.create!` (bypasses `Messages::MessageBuilder`'s
incoming-on-non-Api-inbox guard, which would otherwise reject the system
bubble); content is `'WhatsApp Call'` for WhatsApp and `'Voice Call'`
for Twilio. Backwards-compatible `perform!` retained for the existing
Twilio call sites.
- `enterprise/app/models/call.rb` — adds `default_ice_servers` (driven
by `VOICE_CALL_STUN_URLS` env), `direction_label` alias for the
`inbound`/`outbound` strings the FE expects, and
`ringing?`/`in_progress?`/`terminal?` predicates used throughout the
pipeline.

**Outgoing-channel guard**
- `app/services/base/send_on_channel_service.rb` — extends
`invalid_message?` to skip messages with `content_type == 'voice_call'`.
Without this, agent-initiated outbound calls (PR-4) would deliver
\"WhatsApp Call\" as a text message to the contact every time.

**Twilio call-site update**
- `enterprise/app/controllers/twilio/voice_controller.rb` — drops the
now-redundant `account: current_account` kwarg from the
`Voice::InboundCallBuilder.perform!` call.

**Tests**
- New: `spec/enterprise/services/whatsapp/incoming_call_service_spec.rb`
(5 examples — calling-disabled, inbound connect, outbound connect,
terminate completed, terminate no-answer, unknown event).
- New:
`spec/enterprise/services/whatsapp/call_permission_reply_service_spec.rb`
(3 examples — accept, reject, calling-disabled).
- Updated: `spec/enterprise/services/voice/inbound_call_builder_spec.rb`
and `spec/enterprise/controllers/twilio/voice_controller_spec.rb` to
drop the `account:` kwarg from call expectations.

## How to test

In `rails console` against an account with a WhatsApp inbox where
`provider_config['calling_enabled']` is true:

```ruby
inbox = Inbox.find(<id>)
params = { calls: [{ id: 'wacid_test', from: '15550001111', event: 'connect',
                     session: { sdp: 'v=0...', sdp_type: 'offer' } }] }
Whatsapp::IncomingCallService.new(inbox: inbox, params: params).perform
# => Conversation + Call (status: 'ringing', provider: 'whatsapp') + voice_call message bubble
# => ActionCable broadcasts `voice_call.incoming` to the assignee or account-wide

# Then terminate it:
Whatsapp::IncomingCallService.new(inbox: inbox,
  params: { calls: [{ id: 'wacid_test', event: 'terminate', duration: 0, terminate_reason: 'no_answer' }] }
).perform
# => Call status flips to 'no_answer', message bubble updates, `voice_call.ended` broadcast fires
```

End-to-end browser flow (Meta → cable → UI) requires the controller from
PR-4 and the frontend from PR-9.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 11:23:57 +05:30
05dd31389e fix(whatsapp): Prevent duplicate conversations from concurrent uploads (#14060)
When a WhatsApp contact starts a new conversation by sending multiple
images at once (an album), each image arrives as a separate webhook.
Because no conversation exists yet, the concurrent workers each pass the
"does a conversation exist?" check and each create their own
conversation — producing one conversation per image instead of one
grouped conversation.

This fix serializes webhook processing per `(inbox, contact)` using a
Redis lock at the job level, so only one webhook at a time can create
the initial conversation for a given contact. Concurrent workers retry
with backoff and append to the same conversation once the lock is
released.

## Closes

- Closes #13261

## How to test

1. On a WhatsApp inbox, ensure there is no active (open) conversation
with a specific test contact — resolve or delete any existing one.
2. From a phone, select 6+ images in the WhatsApp gallery and send them
as a single album to the Chatwoot-connected number.
3. Open the Chatwoot dashboard and confirm exactly **one** new
conversation is created, with all images grouped under it.
4. Repeat the test with a mix of attachment types (XMLs, PDFs, images)
sent in rapid succession — still one conversation.

## What changed

- New Redis key `WHATSAPP_MESSAGE_CREATE_LOCK::<inbox_id>::<sender_id>`
in `lib/redis/redis_keys.rb`.
- `Webhooks::WhatsappEventsJob` now inherits from `MutexApplicationJob`
and wraps event processing in `with_lock(key)`, matching the pattern
already used by `FacebookEventsJob`, `InstagramEventsJob`, and
`TiktokEventsJob`.
- Uses `retry_on LockAcquisitionError, wait: 1.second, attempts: 8` so
concurrent webhooks retry until the lock is free instead of poll-waiting
inside the service.
- Sender ID is derived from the webhook payload (contact's `from`, or
`to` for SMB echo events); status-only webhooks bypass the lock.
- Issue 1 from the report (same `source_id` redelivery) was already
handled previously by `Whatsapp::MessageDedupLock` (atomic `SET NX EX`);
no changes needed there.

---------

Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 10:30:27 +04:00
Muhsin KelothandGitHub b686d14044 feat: Handle external echo messages from native apps (#13371)
When businesses use WhatsApp Business App (co-existence mode) or
Instagram App or TikTok alongside Chatwoot, messages sent from the
native apps were not synced properly back to Chatwoot. This left agents
with an incomplete conversation history and no visibility into responses
sent outside the dashboard. Additionally, if these echo messages did
arrive, they appeared as "Sent by: Bot" in the UI since they had no
sender, making it confusing for agents.

This PR subscribes to WhatsApp `smb_message_echoes` webhook events and
routes them through the existing service with an `outgoing_echo` flag,
mirroring how Instagram already handles echoes. On the Instagram side,
echo messages now also carry the `external_echo` content attribute and
`delivered` status.

On the frontend, messages with `externalEcho` are distinguished from bot
messages showing a "Native app" avatar and an advisory note encouraging
agents to reply from Chatwoot to maintain the service window.

<img width="1518" height="524" alt="CleanShot 2026-01-29 at 13 37 57@2x"
src="https://github.com/user-attachments/assets/5aa0b552-6382-441f-96aa-9a62ca716e4a"
/>


Fixes
https://linear.app/chatwoot/issue/CW-4204/display-messages-not-sent-from-chatwoot-in-case-of-outgoing-echo
Fixes
https://linear.app/chatwoot/issue/PLA-33/incoming-from-me-messages-from-whatsapp-business-app-are-not-falling
2026-02-02 15:52:53 +05:30
ca5e112a8c feat: TikTok channel (#12741)
fixes: #11834

This pull request introduces TikTok channel integration, enabling users
to connect and manage TikTok business accounts similarly to other
supported social channels. The changes span backend API endpoints,
authentication helpers, webhook handling, configuration, and frontend
components to support TikTok as a first-class channel.


**Key Notes**
* This integration is only compatible with TikTok Business Accounts
* Special permissions are required to access the TikTok [Business
Messaging
API](https://business-api.tiktok.com/portal/docs?id=1832183871604753).
* The Business Messaging API is region-restricted and is currently
unavailable to users in the EU.
* Only TEXT, IMAGE, and POST_SHARE messages are currently supported due
to limitations in the TikTok Business Messaging API
* A message will be successfully sent only if it contains text alone or
one image attachment. Messages with multiple attachments or those
combining text and attachments will fail and receive a descriptive error
status.
* Messages sent directly from the TikTok App will be synced into the
system
* Initiating a new conversation from the system is not permitted due to
limitations from the TikTok Business Messaging API.


**Backend: TikTok Channel Integration**

* Added `Api::V1::Accounts::Tiktok::AuthorizationsController` to handle
TikTok OAuth authorization initiation, returning the TikTok
authorization URL.
* Implemented `Tiktok::CallbacksController` to handle TikTok OAuth
callback, process authorization results, create or update channel/inbox,
and handle errors or denied scopes.
* Added `Webhooks::TiktokController` to receive and verify TikTok
webhook events, including signature verification and event dispatching.
* Created `Tiktok::IntegrationHelper` module for JWT-based token
generation and verification for secure TikTok OAuth state management.

**Configuration and Feature Flags**

* Added TikTok app credentials (`TIKTOK_APP_ID`, `TIKTOK_APP_SECRET`) to
allowed configs and app config, and registered TikTok as a feature in
the super admin features YAML.
[[1]](diffhunk://#diff-5e46e1d248631a1147521477d84a54f8ba6846ea21c61eca5f70042d960467f4R43)
[[2]](diffhunk://#diff-8bf37a019cab1dedea458c437bd93e34af1d6e22b1672b1d43ef6eaa4dcb7732R69)
[[3]](diffhunk://#diff-123164bea29f3c096b0d018702b090d5ae670760c729141bd4169a36f5f5c1caR74-R79)

**Frontend: TikTok Channel UI and Messaging Support**

* Added `TiktokChannel` API client for frontend TikTok authorization
requests.
* Updated channel icon mappings and tests to include TikTok
(`Channel::Tiktok`).
[[1]](diffhunk://#diff-b852739ed45def61218d581d0de1ba73f213f55570aa5eec52aaa08f380d0e16R16)
[[2]](diffhunk://#diff-3cd3ae32e94ef85f1f2c4435abf0775cc0614fb37ee25d97945cd51573ef199eR64-R69)
* Enabled TikTok as a supported channel in contact forms, channel
widgets, and feature toggles.
[[1]](diffhunk://#diff-ec59c85e1403aaed1a7de35971fe16b7033d5cd763be590903ebf8f1ca25a010R47)
[[2]](diffhunk://#diff-ec59c85e1403aaed1a7de35971fe16b7033d5cd763be590903ebf8f1ca25a010R69)
[[3]](diffhunk://#diff-725b90ca7e3a6837ec8291e9f57094f6a46b3ee00e598d16564f77f32cf354b0R26-R29)
[[4]](diffhunk://#diff-725b90ca7e3a6837ec8291e9f57094f6a46b3ee00e598d16564f77f32cf354b0R51-R54)
[[5]](diffhunk://#diff-725b90ca7e3a6837ec8291e9f57094f6a46b3ee00e598d16564f77f32cf354b0R68)
* Updated message meta logic to support TikTok-specific message statuses
(sent, delivered, read).
[[1]](diffhunk://#diff-e41239cf8dda36c1bd1066dbb17588ae8868e56289072c74b3a6d7ef5abdd696R23)
[[2]](diffhunk://#diff-e41239cf8dda36c1bd1066dbb17588ae8868e56289072c74b3a6d7ef5abdd696L63-R65)
[[3]](diffhunk://#diff-e41239cf8dda36c1bd1066dbb17588ae8868e56289072c74b3a6d7ef5abdd696L81-R84)
[[4]](diffhunk://#diff-e41239cf8dda36c1bd1066dbb17588ae8868e56289072c74b3a6d7ef5abdd696L103-R107)
* Added support for embedded message attachments (e.g., TikTok embeds)
with a new `EmbedBubble` component and updated message rendering logic.
[[1]](diffhunk://#diff-c3d701caf27d9c31e200c6143c11a11b9d8826f78aa2ce5aa107470e6fdb9d7fR31)
[[2]](diffhunk://#diff-047859f9368a46d6d20177df7d6d623768488ecc38a5b1e284f958fad49add68R1-R19)
[[3]](diffhunk://#diff-c3d701caf27d9c31e200c6143c11a11b9d8826f78aa2ce5aa107470e6fdb9d7fR316)
[[4]](diffhunk://#diff-cbc85e7c4c8d56f2a847d0b01cd48ef36e5f87b43023bff0520fdfc707283085R52)
* Adjusted reply policy and UI messaging for TikTok's 48-hour reply
window.
[[1]](diffhunk://#diff-0d691f6a983bd89502f91253ecf22e871314545d1e3d3b106fbfc76bf6d8e1c7R208-R210)
[[2]](diffhunk://#diff-0d691f6a983bd89502f91253ecf22e871314545d1e3d3b106fbfc76bf6d8e1c7R224-R226)

These changes collectively enable end-to-end TikTok channel support,
from configuration and OAuth flow to webhook processing and frontend
message handling.


------------

# TikTok App Setup & Configuration
1. Grant access to the Business Messaging API
([Documentation](https://business-api.tiktok.com/portal/docs?id=1832184145137922))
2. Set the app authorization redirect URL to
`https://FRONTEND_URL/tiktok/callback`
3. Update the installation config with TikTok App ID and Secret
4. Create a Business Messaging Webhook configuration and set the
callback url to `https://FRONTEND_URL/webhooks/tiktok`
([Documentation](https://business-api.tiktok.com/portal/docs?id=1832190670631937))
. You can do this by calling
`Tiktok::AuthClient.update_webhook_callback` from rails console once you
finish Tiktok channel configuration in super admin ( will be automated
in future )
5. Enable TikTok channel feature in an account

---------

Co-authored-by: Sojan Jose <sojan@pepalo.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
2025-12-17 07:54:50 -08:00
Vishnu NarayananandGitHub d6af182f82 fix: Use contact_id instead of sender_id for Instagram message locks (#12841)
Previously, the lock key for Instagram used sender_id, which for echo
messages (outgoing) would be the account's own ID. This caused all
outgoing messages to compete for the same lock, creating a bottleneck
during bulk messaging.

The fix introduces contact_instagram_id method that correctly identifies
the contact's ID regardless of message direction:
- For echo messages (outgoing): uses recipient.id (the contact)
- For incoming messages: uses sender.id (the contact)

This ensures each conversation has a unique lock, allowing parallel
processing of webhooks while maintaining race condition protection
within individual conversations.

Fixes lock acquisition errors in Sidekiq when processing bulk Instagram
messages.

Fixes
https://linear.app/chatwoot/issue/CW-5931/p0-mutexapplicationjoblockacquisitionerror-failed-to-acquire-lock-for

## Type of change

- [x] Bug fix (non-breaking change which fixes an issue)
2025-11-12 16:56:52 +05:30
Muhsin KelothandGitHub b1893c7d96 fix: Support location messages in Twilio WhatsApp integration (#11830)
Fixes location messages not appearing in conversations when sent via
Twilio. Location messages were being filtered out due to empty body
content and missing parameter handling.

![CleanShot 2025-06-27 at 20 48
12](https://github.com/user-attachments/assets/b5a75796-6937-49bc-b689-7d04f4ea5d09)
2025-06-30 11:35:32 +05:30
ruslanandGitHub b87b7972c1 feat(channel): add support for Telegram Business bots (#10181) (#11663)
Added support for Telegram Business bots. Telegram webhooks from such bots include the business_message field, which we transform into a standard message for Chatwoot. This PR also modifies how we handle replies, attachments, and image uploads when working with Telegram Business bots.

demo: https://drive.google.com/file/d/1Yz82wXBVRtb-mxjXogkUju4hlJbt3qyh/view?usp=sharing&t=4

Fixes #10181
2025-06-16 20:35:23 -07:00
Muhsin KelothGitHubSivin VargheseCopilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>Shivam Mishra
bdcb080e40 feat: Handle instagram test service (#11244)
This PR will handle the Instagram test events. We are using the last
created Instagram channel as the test channel since we don't have any
other channels for testing purposes at the time of Meta approval.



https://github.com/user-attachments/assets/98302b7a-d72c-4950-9660-861a5e08d55f

---------

Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
2025-04-11 19:11:29 +05:30
Muhsin KelothGitHubSivin VargheseCopilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>Shivam Mishra
d827e66453 feat: Instagram Inbox using Instagram Business Login (#11054)
This PR introduces basic minimum version of **Instagram Business
Login**, making Instagram inbox setup more straightforward by removing
the Facebook Page dependency. This update enhances user experience and
aligns with Meta’s recommended best practices.

Fixes
https://linear.app/chatwoot/issue/CW-3728/instagram-login-how-to-implement-the-changes


## Why Introduce Instagram as a Separate Inbox?


Currently, our Instagram integration requires linking an Instagram
account to a Facebook Page, making setup complex. To simplify this
process, Instagram now offers **Instagram Business Login**, which allows
users to authenticate directly with their Instagram credentials.

The **Instagram API with Instagram Login** enables businesses and
creators to send and receive messages without needing a Facebook Page
connection. While an Instagram Business or Creator account is still
required, this approach provides a more straightforward integration
process.

| **Existing Approach (Facebook Login for Business)** | **New Approach
(Instagram Business Login)** |
| --- | --- |
| Requires linking Instagram to a Facebook Page | No Facebook Page
required |
| Users log in via Facebook credentials | Users log in via Instagram
credentials |
| Configuration is more complex | Simpler setup |

Meta recommends using **Instagram Business Login** as the preferred
authentication method due to its easier configuration and improved
developer experience.

---

## Implementation Plan

The core messaging functionality is already in place, but the transition
to **Instagram Business Login** requires adjustments.

### Changes & Considerations

- **API Adjustments**: The Instagram API uses `graph.instagram`, whereas
Koala (our existing library) interacts with `graph.facebook`. We may
need to modify API calls accordingly.
- **Three Main Modules**:
  1. **Instagram Business Login** – Handle authentication flow.
2. **Permissions & Features** – Ensure necessary API scopes are granted.
  3. **Webhooks** – Enable real-time message retrieval.

![CleanShot 2025-03-10 at 21 32
28@2x](https://github.com/user-attachments/assets/1b019001-8d16-4e59-aca2-ced81e98f538)


---

## Instagram Login Flow

1. User clicks **"Create Inbox"** for Instagram.
2. App redirects to the [Instagram Authorization
URL](https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/business-login#embed-the-business-login-url).
3. After authentication, Instagram returns an authorization code.
5. The app exchanges the code for a **long-lived token** (valid for 60
days).
6. Tokens are refreshed periodically to maintain access.
7. Once completed, the app creates an inbox and redirects to the
Chatwoot dashboard.

---

## How to Test the Instagram Inbox

1. Create a new app on [Meta's Developer
Portal](https://developers.facebook.com/apps/).
2. Select **Business** as the app type and configure it.
3. Add the Instagram product and connect a business account.
4. Copy Instagram app ID and Instagram app secret
5. Add the Instagram app ID and Instagram app secret to your app config
via `{Chatwoot installation
url}/super_admin/app_config?config=instagram`
6. Configure Webhooks:
   - Callback URL: `{your_chatwoot_url}/webhooks/instagram`
   - Verify Token: `INSTAGRAM_VERIFY_TOKEN`
- Subscribe to `messages`, `messaging_seen`, and `message_reactions`
events.
7. Set up **Instagram Business Login**:
   - Redirect URL: `{your_chatwoot_url}/instagram/callback`
8. Test inbox creation via the Chatwoot dashboard.


## Troubleshooting & Common Errors

### Insufficient Developer Role Error

- Ensure the Instagram user is added as a developer:
- **Meta Dashboard → App Roles → Roles → Add People → Enter Instagram
ID**

### API Access Deactivated

- Ensure the **Privacy Policy URL** is valid and correctly set.

### Invalid request: Request parameters are invalid: Invalid
redirect_uri

- Please configure the Frontend URL. The Frontend URL does not match the
authorization URL.
---


## To-Do List

- [x] Basic integration setup completed.  
- [x] Enable sending messages via [Messaging
API](https://developers.facebook.com/docs/instagram-platform/instagram-api-with-instagram-login/messaging-api).
- [x] Implement automatic webhook subscriptions on inbox creation.  
- [x] Handle **canceled authorization errors**.  
- [x] Handle all the errors
https://developers.facebook.com/docs/instagram-platform/instagram-graph-api/reference/error-codes
- [x] Dynamically fetch **account IDs** instead of hardcoding them.  
- [x] Prevent duplicate Instagram channel creation for the same account.
- [x] Use **Global Config** instead of environment variables.  
- [x] Explore **Human Agent feature** for message handling.  
- [x] Write and refine **test cases** for all scenarios.  
- [x] Implement **token refresh mechanism** (tokens expire after 60
days).
Fixes https://github.com/chatwoot/chatwoot/issues/10440

---------

Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: Shivam Mishra <scm.mymail@gmail.com>
2025-04-08 10:47:41 +05:30
991b108a35 feat: discard Twilio events when Body parameter is not present (#11096)
- Discard Twilio events when body parameter is not present.

---------

Co-authored-by: Pranav <pranavrajs@gmail.com>
2025-03-17 12:47:11 +05:30
586dc800bb chore: Move Twilio event processing to background job (#11094)
- Twilio events were being processed synchronously, leading to slow API
responses.
- This change moves Twilio event processing to a background job to
improve performance and align with how other events (e.g., WhatsApp) are
handled.

---------

Co-authored-by: Pranav <pranav@chatwoot.com>
2025-03-15 13:51:08 -07:00
29158e32fe chore: Logger for non-existent WhatsApp channels (#11064)
- Add a warning logger for cases where we are getting webhook events for
inactive numbers.
- Add config to discard events for inactive numbers so that the meta
will stop sending events

---------

Co-authored-by: Pranav <pranav@chatwoot.com>
2025-03-12 15:50:38 -07:00
Sojan JoseandGitHub d0631e99a5 chore: Add warning logs when Chatwoot receives events for inactive channels (#11066) 2025-03-12 08:16:47 -07:00
3760f206e8 fix: mutex timeout and error handling (#8770)
Fixes the follow cases 
- The ensure block released the lock even on LockAcquisitionError
- Custom timeout was not allowed

This also refactored the with_lock method, now the key has to be constructed in the parent function itself

Co-authored-by: Sojan Jose <sojan@pepalo.com>
2024-01-24 14:18:21 +04:00
Muhsin KelothandGitHub 6c8dacfa0d feat: Facebook delivery reports (#8136) 2023-11-20 12:22:45 +05:30
Muhsin KelothandGitHub 61e03fa33a feat: Bandwidth sms channel delivery reports (#8198)
Fixes: https://linear.app/chatwoot/issue/CW-2566/delivery-report-for-bandwidth-sms
2023-10-27 14:07:15 -07:00
Muhsin KelothandGitHub 78ce8a4652 feat: Add support for Instagram delivery reports (#8125) 2023-10-18 23:42:34 -07:00
Shivam MishraandGitHub 336584c95a feat: mutex for InstagramEventsJob [CW-2447] (#7828) 2023-09-04 15:02:13 +05:30
Shivam MishraandGitHub 5598b4b27e feat: implement mutex for SlackSendJob (#7783) 2023-08-25 11:58:29 +07:00
Shivam MishraandGitHub 44f6a9ec3c feat: update lock timeout and retry configuration (#7785) 2023-08-24 12:59:47 +07:00
Shivam MishraandGitHub 26ef21a243 feat: locking and retry in FB message parsing (#7701) 2023-08-23 09:48:17 +07:00
Clairton Rodrigo HeinzenandGitHub a25179c342 fix: Instagram events job NoMethodError: undefined method for nil:NilClass (#7105)
- Sometimes tag entry does not have messaging or standby and cause a error
2023-05-18 15:22:01 +05:30
Tejaswini ChileandGitHub 65e91f6a6f fix: Duplicate instagram conversations for echo messages (#7078) 2023-05-15 16:25:15 +05:30
Pranav Raj SandGitHub c5c36af529 fix: Remove fallback phone_number search in WhatsApp event processing (#6904) 2023-04-18 18:30:01 -07:00
Pranav Raj SandGitHub 463c09184c fix: Disable processing events if account is suspended (#6849) 2023-04-07 12:01:03 -07:00
Sojan JoseandGitHub a4c9a2b2f2 chore: Support telegram edited_message events (#6785)
- Add support for telegram edited_message events: Update the user message back in the Chatwoot dashboard when updated by the contact on telegram
2023-03-29 18:48:16 +05:30
Sojan JoseandGitHub 8ea0660862 chore: Add reauthorization prompt for Whatsapp Channel (#5929)
- Add reauthorization prompt for Whatsapp Channel

fixes: #5782
2022-11-24 14:50:32 +03:00
Tejaswini ChileandGitHub 0343acdb7e fix: ensure contact_inbox if contact exists(#5667)
- Fixing Instagram issue for existing contacts in the inbox
2022-10-19 12:56:39 -07:00
Tejaswini ChileandGitHub 9c67814724 Fix: Instagram webhook test event (#5317) 2022-08-22 16:10:14 +05:30
Sojan JoseandGitHub a6c609f43d feat: Support for Whatsapp Cloud API (#4938)
Ability to configure Whatsapp Cloud API Inboxes

fixes: #4712
2022-07-06 21:45:03 +02:00
Sojan JoseandGitHub cf10f3d03b chore: Provider APIs for SMS Channel - Bandwidth (#3889)
fixes: #3888
2022-02-03 15:22:13 -08:00
Sojan JoseandGitHub 3b53ca157c chore: fix the failing user destroy for admin (#3223)
- fix the invitee association on the user
- handle Instagram events with inbox missing
2021-10-16 00:11:53 +05:30
bd7aeba484 chore: Provider API prototype (#3112)
Enabling Support for Whatsapp via 360Dialog as a prototype for the provider APIs. 

Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
2021-10-05 23:35:06 +05:30
Tejaswini ChileandGitHub 40d0b2faf3 feat: Add Instagram Channel (#2955) 2021-10-05 14:35:32 +05:30
Sojan JoseandGitHub 328edd24de chore: Move Facebook event processing to worker (#2988) 2021-09-13 18:05:14 +05:30
Sojan JoseandGitHub 0a38632f14 feat: Line Channel (#2904)
- Ability to configure line bots as a channel in chatwoot
- Receive a message sent to the line bot in chatwoot
- Ability to reply to line users from chatwoot

fixes: #2738
2021-09-11 01:31:17 +05:30
Sojan JoseandGitHub 671c5c931f feat: Telegram Channel (#2901)
- Ability to configure telegram bots as a channel in chatwoot
- Receive a message sent to the telegram bot in chatwoot
- Ability to reply to telegram users from chatwoot
- Receive attachment messages in chatwoot

fixes: #1843
2021-09-10 00:00:52 +05:30