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 only retained for `dislike`.
  • 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 route is:
  • `PUT /api/public/webchat/messages/{message_id}/feedback`
  • Accepted 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.

Current widget mismatch

  • The widget client still calls:
  • `PUT /api/public/webchat/messages/{message_id}/reaction`
  • Widget body:
  • `{ "reaction": "like" | "dislike" | null }`
  • There is no matching `/reaction` route in the current public webchat router.
  • If contact-side reactions are failing in the shipped widget, this path mismatch is the first thing to check.

Widget-side behavior

  • The widget only shows reaction buttons when:
  • `message.role === 'ai'`
  • It does not show them for:
  • `bot`
  • `operator`
  • Clicking the same reaction twice toggles it back to `null`.
  • 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`
  • But its websocket handler currently only patches:
  • `content`
  • `updated_at`
  • It does not reconcile websocket-delivered reaction, comment, or actor 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

  • Aggregate webchat AI feedback stats are available from:
  • `GET /api/organizations/{orgId}/messages/stats`
  • Supported `types` query values are:
  • `total`
  • `likes`
  • `dislikes`
  • Those counts are scoped to:
  • `role = ai`
  • `channel = web_chat`

Start building your AI team