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
kind | Pair with | Use for |
|---|---|---|
FEEDBACK_KIND_THUMB | thumbUp (true/false) | A binary thumb next to each reply |
FEEDBACK_KIND_SCALE | rating (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.
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.