diff --git a/app/controllers/api/v1/accounts/agent_bots_controller.rb b/app/controllers/api/v1/accounts/agent_bots_controller.rb
index c2f919659..de3d10081 100644
--- a/app/controllers/api/v1/accounts/agent_bots_controller.rb
+++ b/app/controllers/api/v1/accounts/agent_bots_controller.rb
@@ -34,6 +34,10 @@ class Api::V1::Accounts::AgentBotsController < Api::V1::Accounts::BaseController
@agent_bot.reload
end
+ def reset_secret
+ @agent_bot.reset_secret!
+ end
+
private
def agent_bot
diff --git a/app/controllers/api/v1/accounts/inboxes_controller.rb b/app/controllers/api/v1/accounts/inboxes_controller.rb
index 4ca9a6af8..c7d3e2737 100644
--- a/app/controllers/api/v1/accounts/inboxes_controller.rb
+++ b/app/controllers/api/v1/accounts/inboxes_controller.rb
@@ -66,6 +66,12 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
head :ok
end
+ def reset_secret
+ return head :not_found unless @inbox.api?
+
+ @inbox.channel.reset_secret!
+ end
+
def destroy
::DeleteObjectJob.perform_later(@inbox, Current.user, request.ip) if @inbox.present?
render status: :ok, json: { message: I18n.t('messages.inbox_deletetion_response') }
diff --git a/app/javascript/dashboard/api/agentBots.js b/app/javascript/dashboard/api/agentBots.js
index de887f415..a16b252de 100644
--- a/app/javascript/dashboard/api/agentBots.js
+++ b/app/javascript/dashboard/api/agentBots.js
@@ -25,6 +25,10 @@ class AgentBotsAPI extends ApiClient {
resetAccessToken(botId) {
return axios.post(`${this.url}/${botId}/reset_access_token`);
}
+
+ resetSecret(botId) {
+ return axios.post(`${this.url}/${botId}/reset_secret`);
+ }
}
export default new AgentBotsAPI();
diff --git a/app/javascript/dashboard/api/inboxes.js b/app/javascript/dashboard/api/inboxes.js
index 079f21815..cc564fe96 100644
--- a/app/javascript/dashboard/api/inboxes.js
+++ b/app/javascript/dashboard/api/inboxes.js
@@ -48,6 +48,10 @@ class Inboxes extends CacheEnabledApiClient {
template,
});
}
+
+ resetSecret(inboxId) {
+ return axios.post(`${this.url}/${inboxId}/reset_secret`);
+ }
}
export default new Inboxes();
diff --git a/app/javascript/dashboard/i18n/locale/en/agentBots.json b/app/javascript/dashboard/i18n/locale/en/agentBots.json
index dc92016ab..c17ec60d0 100644
--- a/app/javascript/dashboard/i18n/locale/en/agentBots.json
+++ b/app/javascript/dashboard/i18n/locale/en/agentBots.json
@@ -63,6 +63,16 @@
"ERROR_MESSAGE": "Could not update bot. Please try again."
}
},
+ "SECRET": {
+ "LABEL": "Webhook Secret",
+ "COPY": "Copy secret to clipboard",
+ "COPY_SUCCESS": "Secret copied to clipboard",
+ "TOGGLE": "Toggle secret visibility",
+ "CREATED_DESC": "Use the secret below to verify webhook signatures. Please copy it now, you can also find it later in the bot settings.",
+ "DONE": "Done",
+ "RESET_SUCCESS": "Webhook secret regenerated successfully",
+ "RESET_ERROR": "Unable to regenerate webhook secret. Please try again"
+ },
"ACCESS_TOKEN": {
"TITLE": "Access Token",
"DESCRIPTION": "Copy the access token and save it securely",
diff --git a/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json b/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json
index d0d1573b3..51f855689 100644
--- a/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json
+++ b/app/javascript/dashboard/i18n/locale/en/inboxMgmt.json
@@ -86,6 +86,14 @@
"PLACEHOLDER": "Please enter your Webhook URL",
"ERROR": "Please enter a valid URL"
},
+ "CHANNEL_WEBHOOK_SECRET": {
+ "LABEL": "Webhook Secret",
+ "COPY": "Copy secret to clipboard",
+ "COPY_SUCCESS": "Secret copied to clipboard",
+ "TOGGLE": "Toggle secret visibility",
+ "RESET_SUCCESS": "Webhook secret regenerated successfully",
+ "RESET_ERROR": "Unable to regenerate webhook secret. Please try again"
+ },
"CHANNEL_DOMAIN": {
"LABEL": "Website Domain",
"PLACEHOLDER": "Enter your website domain (eg: acme.com)"
diff --git a/app/javascript/dashboard/routes/dashboard/settings/agentBots/components/AgentBotModal.vue b/app/javascript/dashboard/routes/dashboard/settings/agentBots/components/AgentBotModal.vue
index be4deb337..50baafc6a 100644
--- a/app/javascript/dashboard/routes/dashboard/settings/agentBots/components/AgentBotModal.vue
+++ b/app/javascript/dashboard/routes/dashboard/settings/agentBots/components/AgentBotModal.vue
@@ -47,6 +47,7 @@ const formState = reactive({
const [showAccessToken, toggleAccessToken] = useToggle();
const accessToken = ref('');
+const botSecret = ref('');
const v$ = useVuelidate(
{
@@ -179,15 +180,21 @@ const handleSubmit = async () => {
: t('AGENT_BOTS.EDIT.API.SUCCESS_MESSAGE');
useAlert(alertKey);
- // Show access token after creation
+ // Show access token and secret after creation
if (isCreate) {
- const { access_token: responseAccessToken, id } = response || {};
+ const {
+ access_token: responseAccessToken,
+ secret: responseSecret,
+ id,
+ } = response || {};
if (id && responseAccessToken) {
accessToken.value = responseAccessToken;
+ botSecret.value = responseSecret || '';
toggleAccessToken(true);
} else {
accessToken.value = '';
+ botSecret.value = '';
dialogRef.value.close();
}
} else {
@@ -212,14 +219,16 @@ const initializeForm = () => {
thumbnail,
bot_config: botConfig,
access_token: botAccessToken,
+ secret: botSecretValue,
} = props.selectedBot;
formState.botName = name || '';
formState.botDescription = description || '';
formState.botUrl = botUrl || botConfig?.webhook_url || '';
formState.botAvatarUrl = thumbnail || '';
- if (botAccessToken && props.type === MODAL_TYPES.EDIT) {
- accessToken.value = botAccessToken;
+ if (props.type === MODAL_TYPES.EDIT) {
+ if (botAccessToken) accessToken.value = botAccessToken;
+ if (botSecretValue) botSecret.value = botSecretValue;
}
} else {
resetForm();
@@ -231,6 +240,24 @@ const onCopyToken = async value => {
useAlert(t('AGENT_BOTS.ACCESS_TOKEN.COPY_SUCCESSFUL'));
};
+const onCopySecret = async value => {
+ await copyTextToClipboard(value || botSecret.value);
+ useAlert(t('AGENT_BOTS.SECRET.COPY_SUCCESS'));
+};
+
+const onResetSecret = async () => {
+ const response = await store.dispatch(
+ 'agentBots/resetSecret',
+ props.selectedBot.id
+ );
+ if (response) {
+ botSecret.value = response.secret;
+ useAlert(t('AGENT_BOTS.SECRET.RESET_SUCCESS'));
+ } else {
+ useAlert(t('AGENT_BOTS.SECRET.RESET_ERROR'));
+ }
+};
+
const onResetToken = async () => {
const response = await store.dispatch(
'agentBots/resetAccessToken',
@@ -247,6 +274,7 @@ const onResetToken = async () => {
const closeModal = () => {
if (!showAccessToken.value) v$.value?.$reset();
accessToken.value = '';
+ botSecret.value = '';
toggleAccessToken(false);
};
@@ -318,6 +346,20 @@ defineExpose({ dialogRef });
/>
+
+
+
+
+
+
+
+ {{ $t('AGENT_BOTS.SECRET.CREATED_DESC') }}
+
+
+
+
+
+
+
+
+
{
+ try {
+ const response = await AgentBotsAPI.resetSecret(botId);
+ commit(types.EDIT_AGENT_BOT, response.data);
+ return response.data;
+ } catch (error) {
+ throwErrorMessage(error);
+ return null;
+ }
+ },
};
export const mutations = {
diff --git a/app/javascript/dashboard/store/modules/inboxes.js b/app/javascript/dashboard/store/modules/inboxes.js
index 3f374dce2..ce24ef653 100644
--- a/app/javascript/dashboard/store/modules/inboxes.js
+++ b/app/javascript/dashboard/store/modules/inboxes.js
@@ -366,6 +366,16 @@ export const actions = {
);
return response.data;
},
+ resetSecret: async ({ commit }, inboxId) => {
+ try {
+ const response = await InboxesAPI.resetSecret(inboxId);
+ commit(types.default.EDIT_INBOXES, response.data);
+ return response.data;
+ } catch (error) {
+ throwErrorMessage(error);
+ return null;
+ }
+ },
};
export const mutations = {
diff --git a/app/jobs/agent_bots/webhook_job.rb b/app/jobs/agent_bots/webhook_job.rb
index d752034a7..4ba67c9bf 100644
--- a/app/jobs/agent_bots/webhook_job.rb
+++ b/app/jobs/agent_bots/webhook_job.rb
@@ -2,11 +2,13 @@ class AgentBots::WebhookJob < WebhookJob
queue_as :high
retry_on RestClient::TooManyRequests, RestClient::InternalServerError, wait: 3.seconds, attempts: 3 do |job, error|
url, payload, webhook_type = job.arguments
- Webhooks::Trigger.new(url, payload, webhook_type || :agent_bot_webhook).handle_failure(error)
+ kwargs = job.arguments.last.is_a?(Hash) ? job.arguments.last : {}
+ Webhooks::Trigger.new(url, payload, webhook_type || :agent_bot_webhook, secret: kwargs[:secret],
+ delivery_id: kwargs[:delivery_id]).handle_failure(error)
end
- def perform(url, payload, webhook_type = :agent_bot_webhook)
- super(url, payload, webhook_type)
+ def perform(url, payload, webhook_type = :agent_bot_webhook, secret: nil, delivery_id: nil)
+ super(url, payload, webhook_type, secret: secret, delivery_id: delivery_id)
rescue RestClient::TooManyRequests, RestClient::InternalServerError => e
Rails.logger.warn("[AgentBots::WebhookJob] attempt #{executions} failed #{e.class.name} payload=#{payload.to_json}")
raise
diff --git a/app/listeners/agent_bot_listener.rb b/app/listeners/agent_bot_listener.rb
index 08ee563af..1492f50ac 100644
--- a/app/listeners/agent_bot_listener.rb
+++ b/app/listeners/agent_bot_listener.rb
@@ -76,6 +76,7 @@ class AgentBotListener < BaseListener
def process_webhook_bot_event(agent_bot, payload)
return if agent_bot.outgoing_url.blank?
- AgentBots::WebhookJob.perform_later(agent_bot.outgoing_url, payload)
+ AgentBots::WebhookJob.perform_later(agent_bot.outgoing_url, payload, :agent_bot_webhook,
+ secret: agent_bot.secret, delivery_id: SecureRandom.uuid)
end
end
diff --git a/app/listeners/webhook_listener.rb b/app/listeners/webhook_listener.rb
index 762eaa6ee..835d03661 100644
--- a/app/listeners/webhook_listener.rb
+++ b/app/listeners/webhook_listener.rb
@@ -122,7 +122,7 @@ class WebhookListener < BaseListener
return if inbox.channel.webhook_url.blank?
WebhookJob.perform_later(inbox.channel.webhook_url, payload, :api_inbox_webhook,
- delivery_id: SecureRandom.uuid)
+ secret: inbox.channel.secret, delivery_id: SecureRandom.uuid)
end
def deliver_webhook_payloads(payload, inbox)
diff --git a/app/models/agent_bot.rb b/app/models/agent_bot.rb
index b839f21b4..63f71615d 100644
--- a/app/models/agent_bot.rb
+++ b/app/models/agent_bot.rb
@@ -8,6 +8,7 @@
# description :string
# name :string
# outgoing_url :string
+# secret :string
# created_at :datetime not null
# updated_at :datetime not null
# account_id :bigint
@@ -21,6 +22,8 @@ class AgentBot < ApplicationRecord
include AccessTokenable
include Avatarable
+ include WebhookSecretable
+
scope :accessible_to, lambda { |account|
account_id = account&.id
where(account_id: [nil, account_id])
@@ -63,3 +66,5 @@ class AgentBot < ApplicationRecord
account.nil?
end
end
+
+AgentBot.include_mod_with('Audit::AgentBot')
diff --git a/app/models/channel/api.rb b/app/models/channel/api.rb
index 17270e560..96d0eb8a5 100644
--- a/app/models/channel/api.rb
+++ b/app/models/channel/api.rb
@@ -7,6 +7,7 @@
# hmac_mandatory :boolean default(FALSE)
# hmac_token :string
# identifier :string
+# secret :string
# webhook_url :string
# created_at :datetime not null
# updated_at :datetime not null
@@ -26,6 +27,7 @@ class Channel::Api < ApplicationRecord
has_secure_token :identifier
has_secure_token :hmac_token
+ include WebhookSecretable
validate :ensure_valid_agent_reply_time_window
validates :webhook_url, length: { maximum: Limits::URL_LENGTH_LIMIT }
diff --git a/app/models/concerns/webhook_secretable.rb b/app/models/concerns/webhook_secretable.rb
new file mode 100644
index 000000000..b60c9b825
--- /dev/null
+++ b/app/models/concerns/webhook_secretable.rb
@@ -0,0 +1,13 @@
+module WebhookSecretable
+ extend ActiveSupport::Concern
+
+ included do
+ has_secure_token :secret
+ encrypts :secret if Chatwoot.encryption_configured?
+ end
+
+ def reset_secret!
+ regenerate_secret
+ reload
+ end
+end
diff --git a/app/models/webhook.rb b/app/models/webhook.rb
index 9586e1053..9ee62f11e 100644
--- a/app/models/webhook.rb
+++ b/app/models/webhook.rb
@@ -22,8 +22,7 @@ class Webhook < ApplicationRecord
belongs_to :account
belongs_to :inbox, optional: true
- has_secure_token :secret
- encrypts :secret if Chatwoot.encryption_configured?
+ include WebhookSecretable
validates :account_id, presence: true
validates :url, uniqueness: { scope: [:account_id] }, format: URI::DEFAULT_PARSER.make_regexp(%w[http https])
diff --git a/app/policies/agent_bot_policy.rb b/app/policies/agent_bot_policy.rb
index 7461f6b2d..516f8dee8 100644
--- a/app/policies/agent_bot_policy.rb
+++ b/app/policies/agent_bot_policy.rb
@@ -26,4 +26,8 @@ class AgentBotPolicy < ApplicationPolicy
def reset_access_token?
@account_user.administrator?
end
+
+ def reset_secret?
+ @account_user.administrator?
+ end
end
diff --git a/app/policies/inbox_policy.rb b/app/policies/inbox_policy.rb
index cace85e5e..d77b183ee 100644
--- a/app/policies/inbox_policy.rb
+++ b/app/policies/inbox_policy.rb
@@ -65,4 +65,8 @@ class InboxPolicy < ApplicationPolicy
def health?
@account_user.administrator?
end
+
+ def reset_secret?
+ @account_user.administrator?
+ end
end
diff --git a/app/views/api/v1/accounts/agent_bots/reset_secret.json.jbuilder b/app/views/api/v1/accounts/agent_bots/reset_secret.json.jbuilder
new file mode 100644
index 000000000..f647ac383
--- /dev/null
+++ b/app/views/api/v1/accounts/agent_bots/reset_secret.json.jbuilder
@@ -0,0 +1 @@
+json.partial! 'api/v1/models/agent_bot', formats: [:json], resource: AgentBotPresenter.new(@agent_bot)
diff --git a/app/views/api/v1/accounts/inboxes/reset_secret.json.jbuilder b/app/views/api/v1/accounts/inboxes/reset_secret.json.jbuilder
new file mode 100644
index 000000000..2ad94ff82
--- /dev/null
+++ b/app/views/api/v1/accounts/inboxes/reset_secret.json.jbuilder
@@ -0,0 +1 @@
+json.partial! 'api/v1/models/inbox', formats: [:json], resource: @inbox
diff --git a/app/views/api/v1/models/_agent_bot.json.jbuilder b/app/views/api/v1/models/_agent_bot.json.jbuilder
index 2547bb2a8..d5dbc91b4 100644
--- a/app/views/api/v1/models/_agent_bot.json.jbuilder
+++ b/app/views/api/v1/models/_agent_bot.json.jbuilder
@@ -7,4 +7,5 @@ json.bot_type resource.bot_type
json.bot_config resource.bot_config
json.account_id resource.account_id
json.access_token resource.access_token if resource.access_token.present?
+json.secret resource.secret if !resource.system_bot? && Current.account_user&.administrator?
json.system_bot resource.system_bot?
diff --git a/app/views/api/v1/models/_inbox.json.jbuilder b/app/views/api/v1/models/_inbox.json.jbuilder
index f0b5b1ea8..619e6a28c 100644
--- a/app/views/api/v1/models/_inbox.json.jbuilder
+++ b/app/views/api/v1/models/_inbox.json.jbuilder
@@ -113,6 +113,7 @@ end
## API Channel Attributes
if resource.api?
json.hmac_token resource.channel.try(:hmac_token) if Current.account_user&.administrator?
+ json.secret resource.channel.try(:secret) if Current.account_user&.administrator?
json.webhook_url resource.channel.try(:webhook_url)
json.inbox_identifier resource.channel.try(:identifier)
json.additional_attributes resource.channel.try(:additional_attributes)
diff --git a/config/routes.rb b/config/routes.rb
index 58c574efe..3e868d6d8 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -88,6 +88,7 @@ Rails.application.routes.draw do
resources :agent_bots, only: [:index, :create, :show, :update, :destroy] do
delete :avatar, on: :member
post :reset_access_token, on: :member
+ post :reset_secret, on: :member
end
resources :contact_inboxes, only: [] do
collection do
@@ -221,6 +222,7 @@ Rails.application.routes.draw do
post :sync_templates, on: :member
get :health, on: :member
post :register_webhook, on: :member
+ post :reset_secret, on: :member
if ChatwootApp.enterprise?
resource :conference, only: %i[create destroy], controller: 'conference' do
get :token, on: :member
diff --git a/db/migrate/20260324070820_add_secret_to_agent_bots.rb b/db/migrate/20260324070820_add_secret_to_agent_bots.rb
new file mode 100644
index 000000000..69e1bb314
--- /dev/null
+++ b/db/migrate/20260324070820_add_secret_to_agent_bots.rb
@@ -0,0 +1,5 @@
+class AddSecretToAgentBots < ActiveRecord::Migration[7.1]
+ def change
+ add_column :agent_bots, :secret, :string
+ end
+end
diff --git a/db/migrate/20260324070828_add_secret_to_channel_api.rb b/db/migrate/20260324070828_add_secret_to_channel_api.rb
new file mode 100644
index 000000000..217e4f1c8
--- /dev/null
+++ b/db/migrate/20260324070828_add_secret_to_channel_api.rb
@@ -0,0 +1,5 @@
+class AddSecretToChannelApi < ActiveRecord::Migration[7.1]
+ def change
+ add_column :channel_api, :secret, :string
+ end
+end
diff --git a/db/migrate/20260324070835_backfill_agent_bot_and_channel_api_secrets.rb b/db/migrate/20260324070835_backfill_agent_bot_and_channel_api_secrets.rb
new file mode 100644
index 000000000..ee89b8c02
--- /dev/null
+++ b/db/migrate/20260324070835_backfill_agent_bot_and_channel_api_secrets.rb
@@ -0,0 +1,15 @@
+class BackfillAgentBotAndChannelApiSecrets < ActiveRecord::Migration[7.1]
+ def up
+ AgentBot.where(secret: nil).find_each do |agent_bot|
+ agent_bot.update!(secret: SecureRandom.urlsafe_base64(24))
+ end
+
+ Channel::Api.where(secret: nil).find_each do |channel|
+ channel.update!(secret: SecureRandom.urlsafe_base64(24))
+ end
+ end
+
+ def down
+ # no-op: removing the columns in the previous migrations handles cleanup
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index c8af2be3e..d0993a55b 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -131,6 +131,7 @@ ActiveRecord::Schema[7.1].define(version: 2026_03_24_102005) do
t.bigint "account_id"
t.integer "bot_type", default: 0
t.jsonb "bot_config", default: {}
+ t.string "secret"
t.index ["account_id"], name: "index_agent_bots_on_account_id"
end
@@ -413,6 +414,7 @@ ActiveRecord::Schema[7.1].define(version: 2026_03_24_102005) do
t.string "hmac_token"
t.boolean "hmac_mandatory", default: false
t.jsonb "additional_attributes", default: {}
+ t.string "secret"
t.index ["hmac_token"], name: "index_channel_api_on_hmac_token", unique: true
t.index ["identifier"], name: "index_channel_api_on_identifier", unique: true
end
diff --git a/enterprise/app/models/enterprise/audit/agent_bot.rb b/enterprise/app/models/enterprise/audit/agent_bot.rb
new file mode 100644
index 000000000..255af7fa1
--- /dev/null
+++ b/enterprise/app/models/enterprise/audit/agent_bot.rb
@@ -0,0 +1,7 @@
+module Enterprise::Audit::AgentBot
+ extend ActiveSupport::Concern
+
+ included do
+ audited associated_with: :account, except: [:secret]
+ end
+end
diff --git a/enterprise/app/models/enterprise/channelable.rb b/enterprise/app/models/enterprise/channelable.rb
index 6fcae73d8..100f1024d 100644
--- a/enterprise/app/models/enterprise/channelable.rb
+++ b/enterprise/app/models/enterprise/channelable.rb
@@ -17,7 +17,7 @@ module Enterprise::Channelable
auditable_id = inbox.id
auditable_type = 'Inbox'
- audited_changes = saved_changes.except('updated_at')
+ audited_changes = saved_changes.except('updated_at', 'secret')
return if audited_changes.blank?
diff --git a/spec/listeners/agent_bot_listener_spec.rb b/spec/listeners/agent_bot_listener_spec.rb
index a9721f9c7..6ab4d3b84 100644
--- a/spec/listeners/agent_bot_listener_spec.rb
+++ b/spec/listeners/agent_bot_listener_spec.rb
@@ -25,8 +25,10 @@ describe AgentBotListener do
context 'when agent bot is configured' do
it 'sends message to agent bot' do
create(:agent_bot_inbox, inbox: inbox, agent_bot: agent_bot)
- expect(AgentBots::WebhookJob).to receive(:perform_later).with(agent_bot.outgoing_url,
- message.webhook_data.merge(event: 'message_created')).once
+ expect(AgentBots::WebhookJob).to receive(:perform_later).with(
+ agent_bot.outgoing_url, message.webhook_data.merge(event: 'message_created'),
+ :agent_bot_webhook, secret: agent_bot.secret, delivery_id: instance_of(String)
+ ).once
listener.message_created(event)
end
@@ -48,8 +50,14 @@ describe AgentBotListener do
it 'sends message to both bots exactly once' do
payload = message.webhook_data.merge(event: 'message_created')
- expect(AgentBots::WebhookJob).to receive(:perform_later).with(agent_bot.outgoing_url, payload).once
- expect(AgentBots::WebhookJob).to receive(:perform_later).with(conversation_bot.outgoing_url, payload).once
+ expect(AgentBots::WebhookJob).to receive(:perform_later).with(
+ agent_bot.outgoing_url, payload, :agent_bot_webhook,
+ secret: agent_bot.secret, delivery_id: instance_of(String)
+ ).once
+ expect(AgentBots::WebhookJob).to receive(:perform_later).with(
+ conversation_bot.outgoing_url, payload, :agent_bot_webhook,
+ secret: conversation_bot.secret, delivery_id: instance_of(String)
+ ).once
listener.message_created(event)
end
@@ -74,9 +82,11 @@ describe AgentBotListener do
it 'sends webhook to the inbox agent bot with changed_attributes' do
create(:agent_bot_inbox, inbox: inbox, agent_bot: agent_bot)
- expect(AgentBots::WebhookJob).to receive(:perform_later).with(agent_bot.outgoing_url,
- conversation.webhook_data.merge(event: 'conversation_updated',
- changed_attributes: nil)).once
+ expect(AgentBots::WebhookJob).to receive(:perform_later).with(
+ agent_bot.outgoing_url,
+ conversation.webhook_data.merge(event: 'conversation_updated', changed_attributes: nil),
+ :agent_bot_webhook, secret: agent_bot.secret, delivery_id: instance_of(String)
+ ).once
listener.conversation_updated(event)
end
end
@@ -93,11 +103,14 @@ describe AgentBotListener do
it 'sends webhook with changed_attributes to the assigned agent bot' do
expected_changed_attributes = [{ 'assignee_agent_bot_id' => { previous_value: nil, current_value: agent_bot.id } }]
- expect(AgentBots::WebhookJob).to receive(:perform_later).with(agent_bot.outgoing_url,
- conversation.webhook_data.merge(
- event: 'conversation_updated',
- changed_attributes: expected_changed_attributes
- )).once
+ expect(AgentBots::WebhookJob).to receive(:perform_later).with(
+ agent_bot.outgoing_url,
+ conversation.webhook_data.merge(
+ event: 'conversation_updated',
+ changed_attributes: expected_changed_attributes
+ ),
+ :agent_bot_webhook, secret: agent_bot.secret, delivery_id: instance_of(String)
+ ).once
listener.conversation_updated(event)
end
end
@@ -121,7 +134,8 @@ describe AgentBotListener do
expect(AgentBots::WebhookJob).to receive(:perform_later)
.with(
agent_bot.outgoing_url,
- conversation.contact_inbox.webhook_data.merge(event: 'webwidget_triggered', event_info: { country: 'US' })
+ conversation.contact_inbox.webhook_data.merge(event: 'webwidget_triggered', event_info: { country: 'US' }),
+ :agent_bot_webhook, secret: agent_bot.secret, delivery_id: instance_of(String)
).once
listener.webwidget_triggered(event)
diff --git a/spec/listeners/webhook_listener_spec.rb b/spec/listeners/webhook_listener_spec.rb
index 51dae239b..a7a64f175 100644
--- a/spec/listeners/webhook_listener_spec.rb
+++ b/spec/listeners/webhook_listener_spec.rb
@@ -59,7 +59,7 @@ describe WebhookListener do
api_event = Events::Base.new(event_name, Time.zone.now, message: api_message)
expect(WebhookJob).to receive(:perform_later).with(
channel_api.webhook_url, api_message.webhook_data.merge(event: 'message_created'),
- :api_inbox_webhook, delivery_id: instance_of(String)
+ :api_inbox_webhook, secret: channel_api.secret, delivery_id: instance_of(String)
).once
listener.message_created(api_event)
end
@@ -112,7 +112,7 @@ describe WebhookListener do
expect(WebhookJob).to receive(:perform_later).with(
channel_api.webhook_url,
api_conversation.webhook_data.merge(event: 'conversation_created'),
- :api_inbox_webhook, delivery_id: instance_of(String)
+ :api_inbox_webhook, secret: channel_api.secret, delivery_id: instance_of(String)
).once
listener.conversation_created(api_event)
end
@@ -348,7 +348,7 @@ describe WebhookListener do
expect(WebhookJob).to receive(:perform_later).with(
channel_api.webhook_url, payload, :api_inbox_webhook,
- delivery_id: instance_of(String)
+ secret: channel_api.secret, delivery_id: instance_of(String)
).once
listener.conversation_typing_on(api_event)
end