Files
chatwoot/config/routes.rb
Tanmay Deep Sharma 61d10044a0 feat: Whatsapp embedded signup (#11612)
## Description

This PR introduces WhatsApp Embedded Signup functionality, enabling
users to connect their WhatsApp Business accounts through Meta's
streamlined OAuth flow without manual webhook configuration. This
significantly improves the user experience by automating the entire
setup process.

**Key Features:**

- Embedded signup flow using Facebook SDK and Meta's OAuth 2.0
- Automatic webhook registration and phone number configuration
- Enhanced provider selection UI with card-based design
- Real-time progress tracking during signup process
- Comprehensive error handling and user feedback


## Required Configuration

The following environment variables must be configured by administrators
before this feature can be used:
Super Admin Configuration (via
super_admin/app_config?config=whatsapp_embedded)

- `WHATSAPP_APP_ID`: The Facebook App ID for WhatsApp Business API
integration
- `WHATSAPP_CONFIGURATION_ID`: The Configuration ID for WhatsApp
Embedded Signup flow (obtained from Meta Developer Portal)
- `WHATSAPP_APP_SECRET`: The App Secret for WhatsApp Embedded Signup
flow (required for token exchange)
![Screenshot 2025-06-09 at 11 21
08 AM](https://github.com/user-attachments/assets/1615fb0d-27fc-4d9e-b193-9be7894ea93a)


## How Has This Been Tested?

#### Backend Tests (RSpec):

- Authentication validation for embedded signup endpoints
- Authorization code validation and error handling
- Missing business parameter validation
- Proper response format for configuration endpoint
- Unauthorized access prevention

#### Manual Test Cases:

- Complete embedded signup flow (happy path)
- Provider selection UI navigation
- Facebook authentication popup handling
- Error scenarios (cancelled auth, invalid business data, API failures)
- Configuration presence/absence behavior

## Related Screenshots:

![Screenshot 2025-06-09 at 7 48
18 PM](https://github.com/user-attachments/assets/34001425-df11-4d78-9424-334461e3178f)
![Screenshot 2025-06-09 at 7 48
22 PM](https://github.com/user-attachments/assets/c09f4964-3aba-4c39-9285-d1e8e37d0e33)
![Screenshot 2025-06-09 at 7 48
32 PM](https://github.com/user-attachments/assets/a34d5382-7a91-4e1c-906e-dc2d570c864a)
![Screenshot 2025-06-09 at 10 43
05 AM](https://github.com/user-attachments/assets/a15840d8-8223-4513-82e4-b08f23c95927)
![Screenshot 2025-06-09 at 10 42
56 AM](https://github.com/user-attachments/assets/8c345022-38b5-44c4-aba2-0cda81389c69)


Fixes
https://linear.app/chatwoot/issue/CW-2131/spec-for-whatsapp-cloud-channels-sign-in-with-facebook

---------

Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: iamsivin <iamsivin@gmail.com>
Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
Co-authored-by: Sojan Jose <sojan@pepalo.com>
2025-07-14 21:37:06 -07:00

573 lines
20 KiB
Ruby

Rails.application.routes.draw do
# AUTH STARTS
mount_devise_token_auth_for 'User', at: 'auth', controllers: {
confirmations: 'devise_overrides/confirmations',
passwords: 'devise_overrides/passwords',
sessions: 'devise_overrides/sessions',
token_validations: 'devise_overrides/token_validations',
omniauth_callbacks: 'devise_overrides/omniauth_callbacks'
}, via: [:get, :post]
## renders the frontend paths only if its not an api only server
if ActiveModel::Type::Boolean.new.cast(ENV.fetch('CW_API_ONLY_SERVER', false))
root to: 'api#index'
else
root to: 'dashboard#index'
get '/app', to: 'dashboard#index'
get '/app/*params', to: 'dashboard#index'
get '/app/accounts/:account_id/settings/inboxes/new/twitter', to: 'dashboard#index', as: 'app_new_twitter_inbox'
get '/app/accounts/:account_id/settings/inboxes/new/microsoft', to: 'dashboard#index', as: 'app_new_microsoft_inbox'
get '/app/accounts/:account_id/settings/inboxes/new/instagram', to: 'dashboard#index', as: 'app_new_instagram_inbox'
get '/app/accounts/:account_id/settings/inboxes/new/:inbox_id/agents', to: 'dashboard#index', as: 'app_twitter_inbox_agents'
get '/app/accounts/:account_id/settings/inboxes/new/:inbox_id/agents', to: 'dashboard#index', as: 'app_email_inbox_agents'
get '/app/accounts/:account_id/settings/inboxes/new/:inbox_id/agents', to: 'dashboard#index', as: 'app_instagram_inbox_agents'
get '/app/accounts/:account_id/settings/inboxes/:inbox_id', to: 'dashboard#index', as: 'app_instagram_inbox_settings'
get '/app/accounts/:account_id/settings/inboxes/:inbox_id', to: 'dashboard#index', as: 'app_email_inbox_settings'
resource :widget, only: [:show]
namespace :survey do
resources :responses, only: [:show]
end
resource :slack_uploads, only: [:show]
end
get '/api', to: 'api#index'
namespace :api, defaults: { format: 'json' } do
namespace :v1 do
# ----------------------------------
# start of account scoped api routes
resources :accounts, only: [:create, :show, :update] do
member do
post :update_active_at
get :cache_keys
end
scope module: :accounts do
namespace :actions do
resource :contact_merge, only: [:create]
end
resource :bulk_actions, only: [:create]
resources :agents, only: [:index, :create, :update, :destroy] do
post :bulk_create, on: :collection
end
namespace :captain do
resources :assistants do
member do
post :playground
end
resources :inboxes, only: [:index, :create, :destroy], param: :inbox_id
resources :scenarios
end
resources :assistant_responses
resources :bulk_actions, only: [:create]
resources :copilot_threads, only: [:index, :create] do
resources :copilot_messages, only: [:index, :create]
end
resources :documents, only: [:index, :show, :create, :destroy]
end
resources :agent_bots, only: [:index, :create, :show, :update, :destroy] do
delete :avatar, on: :member
post :reset_access_token, on: :member
end
resources :contact_inboxes, only: [] do
collection do
post :filter
end
end
resources :assignable_agents, only: [:index]
resource :audit_logs, only: [:show]
resources :callbacks, only: [] do
collection do
post :register_facebook_page
get :register_facebook_page
post :facebook_pages
post :reauthorize_page
end
end
resources :canned_responses, only: [:index, :create, :update, :destroy]
resources :automation_rules, only: [:index, :create, :show, :update, :destroy] do
post :clone
end
resources :macros, only: [:index, :create, :show, :update, :destroy] do
post :execute, on: :member
end
resources :sla_policies, only: [:index, :create, :show, :update, :destroy]
resources :custom_roles, only: [:index, :create, :show, :update, :destroy]
resources :campaigns, only: [:index, :create, :show, :update, :destroy]
resources :dashboard_apps, only: [:index, :show, :create, :update, :destroy]
namespace :channels do
resource :twilio_channel, only: [:create]
end
resources :conversations, only: [:index, :create, :show, :update, :destroy] do
collection do
get :meta
get :search
post :filter
end
scope module: :conversations do
resources :messages, only: [:index, :create, :destroy, :update] do
member do
post :translate
post :retry
end
end
resources :assignments, only: [:create]
resources :labels, only: [:create, :index]
resource :participants, only: [:show, :create, :update, :destroy]
resource :direct_uploads, only: [:create]
resource :draft_messages, only: [:show, :update, :destroy]
end
member do
post :mute
post :unmute
post :transcript
post :toggle_status
post :toggle_priority
post :toggle_typing_status
post :update_last_seen
post :unread
post :custom_attributes
get :attachments
get :inbox_assistant
end
end
resources :search, only: [:index] do
collection do
get :conversations
get :messages
get :contacts
get :articles
end
end
resources :contacts, only: [:index, :show, :update, :create, :destroy] do
collection do
get :active
get :search
post :filter
post :import
post :export
end
member do
get :contactable_inboxes
post :destroy_custom_attributes
delete :avatar
end
scope module: :contacts do
resources :conversations, only: [:index]
resources :contact_inboxes, only: [:create]
resources :labels, only: [:create, :index]
resources :notes
end
end
resources :csat_survey_responses, only: [:index] do
collection do
get :metrics
get :download
end
end
resources :applied_slas, only: [:index] do
collection do
get :metrics
get :download
end
end
resources :custom_attribute_definitions, only: [:index, :show, :create, :update, :destroy]
resources :custom_filters, only: [:index, :show, :create, :update, :destroy]
resources :inboxes, only: [:index, :show, :create, :update, :destroy] do
get :assignable_agents, on: :member
get :campaigns, on: :member
get :agent_bot, on: :member
post :set_agent_bot, on: :member
delete :avatar, on: :member
end
resources :inbox_members, only: [:create, :show], param: :inbox_id do
collection do
delete :destroy
patch :update
end
end
resources :labels, only: [:index, :show, :create, :update, :destroy]
resources :notifications, only: [:index, :update, :destroy] do
collection do
post :read_all
get :unread_count
post :destroy_all
end
member do
post :snooze
post :unread
end
end
resource :notification_settings, only: [:show, :update]
resources :teams do
resources :team_members, only: [:index, :create] do
collection do
delete :destroy
patch :update
end
end
end
namespace :twitter do
resource :authorization, only: [:create]
end
namespace :microsoft do
resource :authorization, only: [:create]
end
namespace :google do
resource :authorization, only: [:create]
end
namespace :instagram do
resource :authorization, only: [:create]
end
namespace :notion do
resource :authorization, only: [:create]
end
namespace :whatsapp do
resource :authorization, only: [:create]
end
resources :webhooks, only: [:index, :create, :update, :destroy]
namespace :integrations do
resources :apps, only: [:index, :show]
resources :hooks, only: [:show, :create, :update, :destroy] do
member do
post :process_event
end
end
resource :slack, only: [:create, :update, :destroy], controller: 'slack' do
member do
get :list_all_channels
end
end
resource :dyte, controller: 'dyte', only: [] do
collection do
post :create_a_meeting
post :add_participant_to_meeting
end
end
resource :shopify, controller: 'shopify', only: [:destroy] do
collection do
post :auth
get :orders
end
end
resource :linear, controller: 'linear', only: [] do
collection do
delete :destroy
get :teams
get :team_entities
post :create_issue
post :link_issue
post :unlink_issue
get :search_issue
get :linked_issues
end
end
resource :notion, controller: 'notion', only: [] do
collection do
delete :destroy
end
end
end
resources :working_hours, only: [:update]
resources :portals do
member do
patch :archive
delete :logo
end
resources :categories
resources :articles do
post :reorder, on: :collection
end
end
resources :upload, only: [:create]
end
end
# end of account scoped api routes
# ----------------------------------
namespace :integrations do
resources :webhooks, only: [:create]
end
resource :profile, only: [:show, :update] do
delete :avatar, on: :collection
member do
post :availability
post :auto_offline
put :set_active_account
post :resend_confirmation
post :reset_access_token
end
end
resource :notification_subscriptions, only: [:create, :destroy]
namespace :widget do
resource :direct_uploads, only: [:create]
resource :config, only: [:create]
resources :campaigns, only: [:index]
resources :events, only: [:create]
resources :messages, only: [:index, :create, :update]
resources :conversations, only: [:index, :create] do
collection do
post :destroy_custom_attributes
post :set_custom_attributes
post :update_last_seen
post :toggle_typing
post :transcript
get :toggle_status
end
end
resource :contact, only: [:show, :update] do
collection do
post :destroy_custom_attributes
patch :set_user
end
end
resources :inbox_members, only: [:index]
resources :labels, only: [:create, :destroy]
namespace :integrations do
resource :dyte, controller: 'dyte', only: [] do
collection do
post :add_participant_to_meeting
end
end
end
end
end
namespace :v2 do
resources :accounts, only: [:create] do
scope module: :accounts do
resources :summary_reports, only: [] do
collection do
get :agent
get :team
get :inbox
get :label
end
end
resources :reports, only: [:index] do
collection do
get :summary
get :bot_summary
get :agents
get :inboxes
get :labels
get :teams
get :conversations
get :conversation_traffic
get :bot_metrics
end
end
resources :live_reports, only: [] do
collection do
get :conversation_metrics
get :grouped_conversation_metrics
end
end
end
end
end
end
if ChatwootApp.enterprise?
namespace :enterprise, defaults: { format: 'json' } do
namespace :api do
namespace :v1 do
resources :accounts do
member do
post :checkout
post :subscription
get :limits
post :toggle_deletion
end
end
end
end
post 'webhooks/stripe', to: 'webhooks/stripe#process_payload'
post 'webhooks/firecrawl', to: 'webhooks/firecrawl#process_payload'
end
end
# ----------------------------------------------------------------------
# Routes for platform APIs
namespace :platform, defaults: { format: 'json' } do
namespace :api do
namespace :v1 do
resources :users, only: [:create, :show, :update, :destroy] do
member do
get :login
post :token
end
end
resources :agent_bots, only: [:index, :create, :show, :update, :destroy] do
delete :avatar, on: :member
end
resources :accounts, only: [:create, :show, :update, :destroy] do
resources :account_users, only: [:index, :create] do
collection do
delete :destroy
end
end
end
end
end
end
# ----------------------------------------------------------------------
# Routes for inbox APIs Exposed to contacts
namespace :public, defaults: { format: 'json' } do
namespace :api do
namespace :v1 do
resources :inboxes do
scope module: :inboxes do
resources :contacts, only: [:create, :show, :update] do
resources :conversations, only: [:index, :create, :show] do
member do
post :toggle_status
post :toggle_typing
post :update_last_seen
end
resources :messages, only: [:index, :create, :update]
end
end
end
end
resources :csat_survey, only: [:show, :update]
end
end
end
get 'hc/:slug', to: 'public/api/v1/portals#show'
get 'hc/:slug/sitemap.xml', to: 'public/api/v1/portals#sitemap'
get 'hc/:slug/:locale', to: 'public/api/v1/portals#show'
get 'hc/:slug/:locale/articles', to: 'public/api/v1/portals/articles#index'
get 'hc/:slug/:locale/categories', to: 'public/api/v1/portals/categories#index'
get 'hc/:slug/:locale/categories/:category_slug', to: 'public/api/v1/portals/categories#show'
get 'hc/:slug/:locale/categories/:category_slug/articles', to: 'public/api/v1/portals/articles#index'
get 'hc/:slug/articles/:article_slug.png', to: 'public/api/v1/portals/articles#tracking_pixel'
get 'hc/:slug/articles/:article_slug', to: 'public/api/v1/portals/articles#show'
# ----------------------------------------------------------------------
# Used in mailer templates
resource :app, only: [:index] do
resources :accounts do
resources :conversations, only: [:show]
end
end
# ----------------------------------------------------------------------
# Routes for channel integrations
mount Facebook::Messenger::Server, at: 'bot'
get 'webhooks/twitter', to: 'api/v1/webhooks#twitter_crc'
post 'webhooks/twitter', to: 'api/v1/webhooks#twitter_events'
post 'webhooks/line/:line_channel_id', to: 'webhooks/line#process_payload'
post 'webhooks/telegram/:bot_token', to: 'webhooks/telegram#process_payload'
post 'webhooks/sms/:phone_number', to: 'webhooks/sms#process_payload'
get 'webhooks/whatsapp/:phone_number', to: 'webhooks/whatsapp#verify'
post 'webhooks/whatsapp/:phone_number', to: 'webhooks/whatsapp#process_payload'
get 'webhooks/instagram', to: 'webhooks/instagram#verify'
post 'webhooks/instagram', to: 'webhooks/instagram#events'
namespace :twitter do
resource :callback, only: [:show]
end
namespace :linear do
resource :callback, only: [:show]
end
namespace :shopify do
resource :callback, only: [:show]
end
namespace :twilio do
resources :callback, only: [:create]
resources :delivery_status, only: [:create]
end
get 'microsoft/callback', to: 'microsoft/callbacks#show'
get 'google/callback', to: 'google/callbacks#show'
get 'instagram/callback', to: 'instagram/callbacks#show'
get 'notion/callback', to: 'notion/callbacks#show'
# ----------------------------------------------------------------------
# Routes for external service verifications
get '.well-known/assetlinks.json' => 'android_app#assetlinks'
get '.well-known/apple-app-site-association' => 'apple_app#site_association'
get '.well-known/microsoft-identity-association.json' => 'microsoft#identity_association'
# ----------------------------------------------------------------------
# Internal Monitoring Routes
require 'sidekiq/web'
require 'sidekiq/cron/web'
devise_for :super_admins, path: 'super_admin', controllers: { sessions: 'super_admin/devise/sessions' }
devise_scope :super_admin do
get 'super_admin/logout', to: 'super_admin/devise/sessions#destroy'
namespace :super_admin do
root to: 'dashboard#index'
resource :app_config, only: [:show, :create]
# order of resources affect the order of sidebar navigation in super admin
resources :accounts, only: [:index, :new, :create, :show, :edit, :update, :destroy] do
post :seed, on: :member
post :reset_cache, on: :member
end
resources :users, only: [:index, :new, :create, :show, :edit, :update, :destroy] do
delete :avatar, on: :member, action: :destroy_avatar
end
resources :access_tokens, only: [:index, :show]
resources :installation_configs, only: [:index, :new, :create, :show, :edit, :update]
resources :agent_bots, only: [:index, :new, :create, :show, :edit, :update, :destroy] do
delete :avatar, on: :member, action: :destroy_avatar
end
resources :platform_apps, only: [:index, :new, :create, :show, :edit, :update, :destroy]
resource :instance_status, only: [:show]
resource :settings, only: [:show] do
get :refresh, on: :collection
end
# resources that doesn't appear in primary navigation in super admin
resources :account_users, only: [:new, :create, :show, :destroy]
end
authenticated :super_admin do
mount Sidekiq::Web => '/monitoring/sidekiq'
end
end
namespace :installation do
get 'onboarding', to: 'onboarding#index'
post 'onboarding', to: 'onboarding#create'
end
# ---------------------------------------------------------------------
# Routes for swagger docs
get '/swagger/*path', to: 'swagger#respond'
get '/swagger', to: 'swagger#respond'
# ----------------------------------------------------------------------
# Routes for testing
resources :widget_tests, only: [:index] unless Rails.env.production?
end