Technical docs
Message Reactions
Overview
Support reference for the current message feedback and reaction paths.
What the backend actually supports
- Feedback state now lives on the `messages` table itself:
- `reaction`
- `reaction_at`
- `feedback_comment`
- `feedback_comment_at`
- `feedback_state_updated_at`
- `reaction_by_*`
- Accepted reactions are:
- `like`
- `dislike`
- `null` to clear feedback
- `feedback_comment` is retained for any non-null reaction when a comment is provided.
- If a non-null reaction update omits the comment field, backend feedback state keeps the previously stored comment.
- Clearing the reaction clears the entire feedback state.
Eligibility and validation
- Backend feedback is rejected for:
- `operator` messages
- `contact` messages
- That means backend eligibility currently allows feedback on assistant-side `ai` and `bot` messages.
- `feedback_comment` is capped at `500` characters.
Public webchat API
- Public webchat feedback routes are:
- `PUT /api/public/webchat/messages/{message_id}/feedback`
- `PUT /api/public/webchat/messages/{message_id}/reaction`
- Both routes use the same processor and accept body:
- `{ "reaction": "like" | "dislike" | null, "feedback_comment"?: string | null }`
- The processor verifies that:
- the public API key resolves an organization
- the `webchat-token` resolves a webchat session/contact
- the message belongs to a conversation accessible through that contact channel
- The route returns the full updated webchat message object, not just the reaction value.
Widget-side behavior
- The widget currently calls:
- `PUT /api/public/webchat/messages/{message_id}/reaction`
- Widget request body can include:
- `reaction`
- optional `feedback_comment`
- `conversation_id`
- `event_id`
- optional `contact`
- The widget shows reaction buttons when:
- `message.role === 'ai'`
- `message.role === 'bot'`
- It does not show them for:
- `operator`
- `contact`
- Clicking the same reaction twice toggles it back to `null`.
- After setting a non-null reaction, the widget can open a structured feedback composer for both `Like` and `Dislike`.
- Current widget feedback UX includes:
- preset reason chips per reaction
- optional free-text detail via `Other`
- `Not now`, `Send`, and later `Edit` actions
- The widget updates reaction state optimistically in local component state.
Propagation to Omni and the widget
- Public webchat feedback updates emit:
- `conversation.message_updated` to organization websocket clients
- Omni consumes that event and reads reaction fields from the updated message payload.
- Omni renders feedback as read-only footer state on outgoing messages.
- The widget listens for:
- `webchat.message_updated`
- Current widget websocket updates now merge the full updated message payload into the active transcript, including reaction and feedback-comment fields.
Internal chat feedback path
- Internal chat uses the organization route:
- `PUT /api/organizations/{orgId}/conversations/{conversationId}/messages/{messageId}/feedback`
- That flow emits:
- `internal.message_updated`
- Internal chat feedback also adds rate limiting for repeated changes on the same message.
Reporting API
- Org admin reporting endpoints are:
- `GET /api/organizations/{orgId}/chat-feedback/events`
- `GET /api/organizations/{orgId}/chat-feedback/summary`
- Both routes require authenticated org access plus org-admin privileges.
- Current event-list query supports:
- `target_kind`
- `target_id`
- `employee_id`
- `conversation_channel`
- `reaction`
- `change_kind`
- `from`
- `to`
- `has_comment`
- `search`
- `page`
- `limit`
- Summary route reuses the same filters except pagination.