# Answer quality

What visitors think of the assistant's answers, and a queue of conversations staff should look at.

## Visitor feedback

- **Thumbs on answers.** The stock widget shows a thumbs up/down under the last answer of each
  assistant turn. A thumbs down asks, optionally, what was missing. Tapping the chosen thumb again
  takes it back. One per answer (`message_feedback`).
- **Rating a conversation.** When a conversation is closed (by staff, or by the visitor with
  «Avslutt samtalen» in the chat list), the widget asks «Hvordan var samtalen?» with 1–5 stars. A
  star saves at once; a comment can follow. One per conversation, changeable
  (`conversation_ratings`). The transcript gets a staff-only system message `kind: "rating"` with
  `rating: { score, comment }`; changing the rating updates that message.
- Every thumb and rating records a `rating.created` event (`target: "message" | "conversation"`,
  `updated: true` on a change), so notifications and webhooks can react to it.

API (visitor token):

| Method | Path | Body |
| --- | --- | --- |
| `PUT` | `/visitor/conversations/:id/messages/:messageId/feedback` | `{ rating: "up" \| "down", comment? }` |
| `DELETE` | `/visitor/conversations/:id/messages/:messageId/feedback` | |
| `PUT` | `/visitor/conversations/:id/rating` | `{ score: 1–5, comment? }` → `{ score, comment }` |
| `POST` | `/visitor/conversations/:id/close` | |

SDK: `rateMessage`, `rateConversation`, `endConversation` on the visitor client. Assistant
messages carry `feedback`; `GET /visitor/conversations/:id` returns the visitor's own `rating`
(the `rating` system message itself is never sent to visitors).

## Review queue (admin «Kvalitet»)

`GET /operator/quality/items?status=open|reviewed|all&days=30` lists, newest first:

| Kind | When |
| --- | --- |
| `thumbs_down` | the visitor gave an answer a thumbs down (with their comment) |
| `low_rating` | the visitor rated the conversation 1 or 2 |
| `handoff` | the assistant asked for a person (`overfor_til_menneske`), with its note |
| `requested` | the visitor asked for a person (one item per conversation, with `handoff`) |
| `no_answer` | every search the assistant made in a turn came back empty |

Items are derived from feedback and messages by `snakkbar_quality_items`; nothing is copied.
Each carries the visitor's question, the assistant's answer and the team's reply after it.
`PATCH /operator/quality/items/:key { reviewed }` marks one as reviewed (`quality_reviews`).

In the admin, an item opens the conversation scrolled to its message, and «Lag kunnskapsforslag»
opens the suggestion editor prefilled with the question and the team's answer.

`no_answer` comes from `server/turn.ts`: when all of a turn's `sok_produkter`, `finn_sider` and
`sok_dokumenter` results are empty, the stored answer gets `meta.searchMiss.searches`.

## Numbers

`GET /operator/quality/summary?days=30` (`snakkbar_quality_summary`, same period rules as the
dashboard): `assistantAnswers`, `thumbsUp`, `thumbsDown`, `thumbsUpRatio`, `ratings`,
`averageRating`, `ratingCounts` (1–5), `conversations`, `handoffs`, `handoffRate`,
`searchMisses`, `openItems`. `GET /operator/dashboard` includes the same numbers for its period
as `quality` (null if they could not be computed), and the Oversikt view shows them as tiles;
`snakkbar_dashboard` itself is unchanged.
