fix(agent-bot): Include changed_attributes in conversation_updated webhook (#14001)

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>
This commit is contained in:
Muhsin Keloth
2026-04-06 11:14:09 +04:00
committed by GitHub
parent 5fd3d5e036
commit f4d66566d0
2 changed files with 20 additions and 6 deletions

View File

@@ -17,9 +17,10 @@ class AgentBotListener < BaseListener
def conversation_updated(event)
conversation = extract_conversation_and_account(event)[0]
changed_attributes = extract_changed_attributes(event)
inbox = conversation.inbox
event_name = __method__.to_s
payload = conversation.webhook_data.merge(event: event_name)
payload = conversation.webhook_data.merge(event: event_name, changed_attributes: changed_attributes)
agent_bots_for(inbox, conversation).each { |agent_bot| process_webhook_bot_event(agent_bot, payload) }
end

View File

@@ -59,9 +59,10 @@ describe AgentBotListener do
describe '#conversation_updated' do
let(:event_name) { 'conversation.updated' }
let!(:event) { Events::Base.new(event_name, Time.zone.now, conversation: conversation) }
context 'when agent bot is not configured' do
let!(:event) { Events::Base.new(event_name, Time.zone.now, conversation: conversation) }
it 'does not send webhook' do
expect(AgentBots::WebhookJob).not_to receive(:perform_later)
listener.conversation_updated(event)
@@ -69,22 +70,34 @@ describe AgentBotListener do
end
context 'when agent bot is configured on inbox' do
it 'sends webhook to the inbox agent bot' do
let!(:event) { Events::Base.new(event_name, Time.zone.now, conversation: conversation) }
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')).once
conversation.webhook_data.merge(event: 'conversation_updated',
changed_attributes: nil)).once
listener.conversation_updated(event)
end
end
context 'when conversation is assigned to an agent bot' do
let!(:event) do
Events::Base.new(event_name, Time.zone.now, conversation: conversation,
changed_attributes: { 'assignee_agent_bot_id' => [nil, agent_bot.id] })
end
before do
conversation.update!(assignee_agent_bot: agent_bot, assignee: nil)
end
it 'sends webhook to the assigned agent bot' 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')).once
conversation.webhook_data.merge(
event: 'conversation_updated',
changed_attributes: expected_changed_attributes
)).once
listener.conversation_updated(event)
end
end