Skip to main content

Message Feedback

The thumbs-up next to an assistant reply. Ratings are attached to a specific message, attributed to the rater, and travel with the conversation — so the same signal that renders a filled-in thumb in your UI is also what shows up in Evals as SCORE_SOURCE_USER.

Only ROLE_ASSISTANT messages can be rated.

Rate a Message

Messages are addressed by their per-conversation messageSequence, not by an ID.

curl -X POST https://api.travila.ai/api/v1/llm/rate-message \
-H "X-API-Key: sk_your_key_here" \
-H "X-On-Behalf-Of: user_123" \
-H "Content-Type: application/json" \
-d '{
"conversationKey": "support-chat-001",
"messageSequence": 8,
"kind": "FEEDBACK_KIND_THUMB",
"thumbUp": true
}'

Response:

{
"ratedMessage": {
"role": "ROLE_ASSISTANT",
"sequence": "8",
"content": [{"type": "CONTENT_PART_TYPE_TEXT", "content": "…"}],
"feedback": [
{
"kind": "FEEDBACK_KIND_THUMB",
"thumbUp": true,
"ratedAt": "2026-08-14T11:02:44Z",
"ratedBy": "user_123"
}
]
},
"isUpdate": true
}

ratedMessage.feedback is the full, all-rater list, so you can re-render the message straight from the response. isUpdate is true when this replaced the caller's own previous rating on that message.

Two rating styles

kindPair withUse for
FEEDBACK_KIND_THUMBthumbUp (true/false)A binary thumb next to each reply
FEEDBACK_KIND_SCALErating (1–10)A finer-grained survey prompt
{
"conversationKey": "support-chat-001",
"messageSequence": 8,
"kind": "FEEDBACK_KIND_SCALE",
"rating": 9,
"reason": "Answered the question and cited the policy."
}

reason is optional free text, up to 1000 characters, and is available on the entry afterwards — worth collecting on thumbs-down, where the score alone tells you nothing actionable.

The rater is never a parameter

There is deliberately no "who is rating" field. ratedBy is server-stamped from the gateway-verified x-user-id, and it is the attribution key: one entry per rater per message. Re-rating replaces the caller's previous entry rather than appending a second one, so a client can send on every tap without deduplicating.

Withdraw a Rating

The user tapping an already-filled thumb to un-set it.

curl -X POST https://api.travila.ai/api/v1/llm/delete-message-rating \
-H "X-API-Key: sk_your_key_here" \
-H "X-On-Behalf-Of: user_123" \
-H "Content-Type: application/json" \
-d '{
"conversationKey": "support-chat-001",
"messageSequence": 8
}'

Response:

{
"ratedMessage": {
"role": "ROLE_ASSISTANT",
"sequence": "8",
"content": [{"type": "CONTENT_PART_TYPE_TEXT", "content": "…"}]
},
"removed": true
}

It removes only the caller's own entry; other raters' feedback stays. Withdrawing a rating that was never left is an idempotent success, not an error — you get removed: false, and since false is omitted by protojson, that arrives as the field being absent.

Reading Ratings Back

feedback is populated on assistant messages returned by conversation-state, so rendering a history with its thumbs already filled in takes no extra call.

It is read-only on that path: feedback sent on send-message is ignored. These two endpoints are the only way to write it.

For aggregate analysis — how a profile scores across conversations, user ratings next to reviewer ratings — read them through the Evals scores API instead of walking conversations.