Feedback, titles and message actions
A chat surface — a portal, an embedded widget, your own client — is what people actually talk to an agent through. It decides what to switch on; the control plane provides the pieces: the thumbs people give an answer, a title for each conversation, and the actions on a message — regenerate and edit.
This page documents the runtime's part of that: what a client can read and send, and what the platform records when a surface uses these features. The surface itself is documented with the module that provides it. The welcome text, the suggested first messages and the tools of an embedding page are not settings of the agent: a portal sets them for itself, per agent.
Feedback
A person can rate an answer helpful or not helpful. A thumbs down can carry reasons — inaccurate, incomplete, off_topic, too_long, unsafe, other — and a comment of up to 1000 characters.
- One rating per person per answer. Rating again replaces it; withdrawing (a
nullrating) deletes it. - An answer is addressed by a stable message id (
msg_and 20 hex characters), derived from the session, its place in the transcript and its time, so rating never touches the transcript. It is theidof the message item a response returns. - When an answer is replaced (regenerate, edit), the ratings of the replaced messages stay and count; they are marked as removed (Answer replaced in Studio).
In Studio
- Sessions → a session shows the ratings under the answer they belong to.
- The agent's Feedback tab shows the counts, the share of helpful answers, why answers were not helpful, and the latest ratings with an Open in session link. It can be narrowed by period (Last N days), rating (All / Helpful / Not helpful), reason, and Only with a comment. The tab is not shown for connected (external) agents.
- Counts and reasons are visible to everyone who can read the agent; the words people wrote, and who wrote them, only to those who can change it (
canReadCommentsin the responses says which).
Dashboard routes
| Route | |
|---|---|
GET /api/agents/{agentId}/feedback | Latest ratings, newest first: { items, total, canReadComments }. Query: days (1–365, default 30), rating, reason, hasComment, portalId, limit (default 25, at most 100), skip. |
GET /api/agents/{agentId}/feedback/summary | Counts: up, down, total, positiveRate, withComment, reasons. Same filters. |
GET /api/agents/{agentId}/sessions/{sessionId}/feedback | Every rating of one session. |
These authenticate with a browser session. Surfaces record feedback through the platform's recordMessageFeedback service function — it takes the session, the message, the person, the channel (portal, embed, …) and whether comments are allowed on that surface. A client with an API token sends a rating through the client API below.
From your own client (API)
A client that calls the agent through the API can send the same rating. The id of the message item in a response (output[].id) is the id to rate:
curl -X PUT "$CONSOLE_URL/api/client/v1/agents/sessions/$SESSION_ID/messages/$MESSAGE_ID/feedback" \
-H "Authorization: Bearer $COGNIPEER_API_TOKEN" -H "Content-Type: application/json" \
-d '{ "rating": "down", "reasons": ["inaccurate"], "comment": "The date is wrong." }'{
"object": "agent.message.feedback",
"session_id": "…",
"message_id": "msg_c4059d953c4924553800",
"rating": "down",
"reasons": ["inaccurate"],
"comment": "The date is wrong.",
"created_at": "2026-10-12T13:35:27.087Z",
"updated_at": "2026-10-12T13:35:27.087Z"
}rating | up or down (required). |
reasons | Any of the six reasons above. Kept with down. |
comment | Free text, up to 1000 characters. The API always accepts comments; a portal can turn them off for itself. |
201 for a new rating, 200 when it replaced the person's earlier one. DELETE on the same path withdraws it ({ "deleted": true }).
Who can rate. Only the person the session belongs to: the token's owner, or the end user a delegating token acts for. Anyone else gets 403 not_session_owner. The rating is the same record the portal writes (marked channel: api), so it shows in the agent's Feedback tab and sends the same trace event, audit entry and webhook. The errors are the ones in the table below, plus 403 and the usual 401 / 404 for the token and the session.
Where it is announced
Recording or withdrawing a rating:
- writes a
feedbackevent to the trace of the turn that wrote the answer (so the trace in Observability shows it); - adds an audit entry (
agent.message.feedback, oragent.message.feedback.withdraw) with no comment text; - sends the
feedback.recordedwebhook — ids, rating, reasons and whether there is a comment, never the comment itself.
Feedback errors
| Status | Code | Meaning |
|---|---|---|
400 | invalid_rating | rating must be "up", "down" or null. |
400 | invalid_reasons, invalid_comment, comment_too_long | A reason is not one of the known six, or the comment is not text or is over 1000 characters. |
400 | comments_disabled | The surface does not take comments (Comments are turned off here.). |
400 | not_an_answer | Only an assistant answer can be rated. |
404 | session_not_found, message_not_found | No such session, or the message is not in it. |
Titles
generateConversationTitle asks a model for a title of at most six words, in the language the conversation is in (cut to 80 characters). The call is recorded like any other model call — a usage row tagged purpose: title, route agent.title — attributed to the person, and counted against the tenant's spend limits. generateAndStoreConversationTitle names a conversation once, after its first exchange, and leaves a title a person chose (metadata.titleSource: user). A model that is gone, a spend limit or a timeout means "no generated title": the conversation keeps the one it has. Which model writes titles — off, the agent's own, or a model key — is the surface's setting.
Cutting a session back, regenerating, editing
Three things a chat surface offers on top of "send a message":
truncateSessionAfterMessagekeeps the messages up to an index and removes the rest. Requests still open on the removed messages are cancelled, the agent's carried state is dropped (the next turn rebuilds it from the transcript), the files the removed messages carried are remembered as already sent, the ratings of the removed messages are marked, andsession.truncatedis written to the event log.rerunSessionTurncuts the session back to just before a message and runs it again. Regenerate takes the latest answer, runs its user message again (tools included) and keeps the answer it replaced aspreviousVersionson the new message (newest last, at most 5). Edit takes a user message, drops everything after it and runs the new text, with the original files unless others are given. If the new turn cannot run at all, the transcript is put back.- All of it needs the conversation's run slot, exactly like sending a message: a session that is still answering is refused with
409 conversation_busy. Other refusals:404 message_not_found,409 not_latest_answer(only the latest answer can be regenerated), and400fornot_a_user_message,not_an_assistant_messageandempty_message.
These are service functions the surface calls — there is no plain client-API route for them.
What's new
Message feedback with its Studio tab, webhook event, audit trail and a client API route to send a rating, generated titles, and truncate / regenerate / edit with the session.truncated event are new in the October 2026 agent runtime release.

