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.

Start building your AI team