result = client.query(
database="acme_corp",
query="What is our refund policy?",
)
client.feedback.submit(
request_id=result.meta.request_id,
feedback="Returned the 2023 policy - the current one is in the Q3 handbook.",
rating="negative",
source="agent",
database="acme_corp",
metadata={"agent": "support-bot", "conversation": "c-8891"},
)
const result = await client.query({
database: "acme_corp",
query: "What is our refund policy?",
});
await client.feedback.submit({
requestId: result.meta.requestId,
feedback: "Returned the 2023 policy - the current one is in the Q3 handbook.",
rating: "negative",
source: "agent",
database: "acme_corp",
metadata: { agent: "support-bot", conversation: "c-8891" },
});
curl -X POST 'https://api.hydradb.com/feedback' \
-H "Authorization: Bearer <your_api_key>" \
-H "API-Version: 2" \
-H "Content-Type: application/json" \
-d '{
"request_id": "9d13aef4-02f4-4e73-8c62-4c2601d04f9d",
"feedback": "Returned the 2023 policy - the current one is in the Q3 handbook.",
"rating": "negative",
"source": "agent",
"database": "acme_corp",
"metadata": { "agent": "support-bot" }
}'
{
"success": true,
"data": {
"feedback_id": "0f5c2e18-7b41-4a92-9d0c-5e1f7a3b6c84",
"request_id": "9d13aef4-02f4-4e73-8c62-4c2601d04f9d",
"recorded": true,
"created_at": "2026-08-10T09:11:55.593287923Z",
"message": "Feedback recorded. Thank you - it is used to validate and improve retrieval quality."
},
"error": null,
"meta": {
"request_id": "84467490-be20-4640-8693-f604a03227cc",
"api_version": "2.0.1",
"latency_ms": 102.1
}
}
{
"success": false,
"data": null,
"error": {
"code": "INVALID_INPUT",
"message": "request_id \"abc-123\" is not a valid request id: it must be the UUID returned in response.meta.request_id (or the X-Request-ID header) of the query you are giving feedback on"
},
"meta": {
"request_id": "b4340e7c-0082-4c20-829d-5fd53f81a54d",
"api_version": "2.0.1"
}
}
{
"success": false,
"data": null,
"error": {
"code": "RATE_LIMITED",
"message": "feedback per_min rate limit exceeded (limit: 100). Please retry in 37 second(s)."
},
"meta": {
"request_id": "7c1e9a44-0b2d-4f88-a3e1-9d6c2b5f0e73",
"api_version": "2.0.1"
}
}
Submit Feedback
Tell HydraDB how a query performed, so retrieval quality improves.
result = client.query(
database="acme_corp",
query="What is our refund policy?",
)
client.feedback.submit(
request_id=result.meta.request_id,
feedback="Returned the 2023 policy - the current one is in the Q3 handbook.",
rating="negative",
source="agent",
database="acme_corp",
metadata={"agent": "support-bot", "conversation": "c-8891"},
)
const result = await client.query({
database: "acme_corp",
query: "What is our refund policy?",
});
await client.feedback.submit({
requestId: result.meta.requestId,
feedback: "Returned the 2023 policy - the current one is in the Q3 handbook.",
rating: "negative",
source: "agent",
database: "acme_corp",
metadata: { agent: "support-bot", conversation: "c-8891" },
});
curl -X POST 'https://api.hydradb.com/feedback' \
-H "Authorization: Bearer <your_api_key>" \
-H "API-Version: 2" \
-H "Content-Type: application/json" \
-d '{
"request_id": "9d13aef4-02f4-4e73-8c62-4c2601d04f9d",
"feedback": "Returned the 2023 policy - the current one is in the Q3 handbook.",
"rating": "negative",
"source": "agent",
"database": "acme_corp",
"metadata": { "agent": "support-bot" }
}'
{
"success": true,
"data": {
"feedback_id": "0f5c2e18-7b41-4a92-9d0c-5e1f7a3b6c84",
"request_id": "9d13aef4-02f4-4e73-8c62-4c2601d04f9d",
"recorded": true,
"created_at": "2026-08-10T09:11:55.593287923Z",
"message": "Feedback recorded. Thank you - it is used to validate and improve retrieval quality."
},
"error": null,
"meta": {
"request_id": "84467490-be20-4640-8693-f604a03227cc",
"api_version": "2.0.1",
"latency_ms": 102.1
}
}
{
"success": false,
"data": null,
"error": {
"code": "INVALID_INPUT",
"message": "request_id \"abc-123\" is not a valid request id: it must be the UUID returned in response.meta.request_id (or the X-Request-ID header) of the query you are giving feedback on"
},
"meta": {
"request_id": "b4340e7c-0082-4c20-829d-5fd53f81a54d",
"api_version": "2.0.1"
}
}
{
"success": false,
"data": null,
"error": {
"code": "RATE_LIMITED",
"message": "feedback per_min rate limit exceeded (limit: 100). Please retry in 37 second(s)."
},
"meta": {
"request_id": "7c1e9a44-0b2d-4f88-a3e1-9d6c2b5f0e73",
"api_version": "2.0.1"
}
}
source labels which one it was.
Linking feedback to a query
Every HydraDB response carries arequest_id in meta, and the same value in the X-Request-ID header. Send that id back and we can line your comment up with the exact query it is about - the text queried, what came back, how long it took.
{
"success": true,
"data": { "chunks": [ /* ... */ ] },
"error": null,
"meta": {
"request_id": "9d13aef4-02f4-4e73-8c62-4c2601d04f9d",
"api_version": "2.0.1",
"latency_ms": 412.8
}
}
request_id back exactly as you received it. It must be the UUID from meta.request_id (or the X-Request-ID header) - any other value is rejected with 400.Fields
result = client.query(
database="acme_corp",
query="What is our refund policy?",
)
client.feedback.submit(
request_id=result.meta.request_id,
feedback="Returned the 2023 policy - the current one is in the Q3 handbook.",
rating="negative",
source="agent",
database="acme_corp",
metadata={"agent": "support-bot", "conversation": "c-8891"},
)
const result = await client.query({
database: "acme_corp",
query: "What is our refund policy?",
});
await client.feedback.submit({
requestId: result.meta.requestId,
feedback: "Returned the 2023 policy - the current one is in the Q3 handbook.",
rating: "negative",
source: "agent",
database: "acme_corp",
metadata: { agent: "support-bot", conversation: "c-8891" },
});
curl -X POST 'https://api.hydradb.com/feedback' \
-H "Authorization: Bearer <your_api_key>" \
-H "API-Version: 2" \
-H "Content-Type: application/json" \
-d '{
"request_id": "9d13aef4-02f4-4e73-8c62-4c2601d04f9d",
"feedback": "Returned the 2023 policy - the current one is in the Q3 handbook.",
"rating": "negative",
"source": "agent",
"database": "acme_corp",
"metadata": { "agent": "support-bot" }
}'
{
"success": true,
"data": {
"feedback_id": "0f5c2e18-7b41-4a92-9d0c-5e1f7a3b6c84",
"request_id": "9d13aef4-02f4-4e73-8c62-4c2601d04f9d",
"recorded": true,
"created_at": "2026-08-10T09:11:55.593287923Z",
"message": "Feedback recorded. Thank you - it is used to validate and improve retrieval quality."
},
"error": null,
"meta": {
"request_id": "84467490-be20-4640-8693-f604a03227cc",
"api_version": "2.0.1",
"latency_ms": 102.1
}
}
{
"success": false,
"data": null,
"error": {
"code": "INVALID_INPUT",
"message": "request_id \"abc-123\" is not a valid request id: it must be the UUID returned in response.meta.request_id (or the X-Request-ID header) of the query you are giving feedback on"
},
"meta": {
"request_id": "b4340e7c-0082-4c20-829d-5fd53f81a54d",
"api_version": "2.0.1"
}
}
{
"success": false,
"data": null,
"error": {
"code": "RATE_LIMITED",
"message": "feedback per_min rate limit exceeded (limit: 100). Please retry in 37 second(s)."
},
"meta": {
"request_id": "7c1e9a44-0b2d-4f88-a3e1-9d6c2b5f0e73",
"api_version": "2.0.1"
}
}
Ground truth
If you already know the right answer - you are running an evaluation set, or you know which document the user needed - send it. It is a much stronger signal than a comment, because we can score it without a human reading it."ground_truth": {
"answer": "Refunds are processed within 14 days.",
"source_ids": ["policy_2024", "handbook_q3"]
}
answer- the response you expected.source_ids- the sources that actually contain the answer. This is the one that grades retrieval: it tells us whether the query surfaced those documents, and where they ranked.
ground_truth is your only signal, at least one of the two has to carry something - values that are empty or all whitespace are treated as not sent.
ground_truth, the feedback comment becomes optional - an evaluation run with an answer key does not need prose for every row. A submission with neither is rejected.for case in eval_set:
result = client.query(database="acme_corp", query=case.question)
try:
client.feedback.submit(
request_id=result.meta.request_id,
source="agent",
ground_truth={
"answer": case.expected_answer,
"source_ids": case.expected_sources,
},
)
except Exception:
continue # one unrecorded case should not abort the run
429 loses every remaining case, not just the one it failed on.
Duplicate source_ids are collapsed and blank entries dropped, so you do not need to de-duplicate or filter your answer key first - a list that still has one real id in it is scored on that id.
Submitting more than once
Each submission is stored separately - a second comment about the same query does not replace the first. Send several as your understanding of a bad result develops, and file feedback from more than one user on the same query.Rate limit
100 submissions per minute per organization. Over that, you get429 with a Retry-After header and a message naming the seconds to wait - it is safe to retry after waiting.
The ceiling is well above normal use; an agent reporting on every query it makes will stay comfortably under it.
Errors
| Status | When |
|---|---|
400 | request_id missing or not a UUID; no usable signal - feedback blank or absent and ground_truth absent, empty, or blank; feedback too long; unknown rating/source; collection without database |
401 | Missing or invalid API key |
404 | database does not exist or is not reachable by this key |
429 | Over the rate limit - see Retry-After |
500 | Feedback could not be stored. Nothing was recorded; retrying is safe |
500 means the submission was not saved, so a retry cannot create a duplicate of something already stored.Authorizations
API key sent as a Bearer token: "Bearer prefix.secret"
Body
Feedback submission
- Option 1
- Option 2
Free-text comment describing what was right or wrong about the results. Required unless ground_truth is supplied.
1 - 8000\SThe request_id from response.meta of the query this feedback is about. Required — it is what links the feedback to the query that ran.
"9d13aef4-02f4-4e73-8c62-4c2601d04f9d"
Optional collection scope for this feedback. A collection is scoped to a database, so database must be sent alongside it; sending collection on its own is rejected.
1"team_docs"
Owning database. Formerly tenant_id; the tenant_id alias is still accepted (deprecated).
1"acme_corp"
What you already know the right answer to be, when you know it. Supply an expected answer, the source_ids that contain it, or both — at least one is required if the field is present. Machine-checkable, so it is a stronger signal than a comment: submit it alone and feedback becomes optional.
Show child attributes
Show child attributes
{ "source_ids": ["HydraDoc1234", "HydraDoc4567"] }
Free-form key-value context stored alongside the feedback (e.g. agent name, conversation or eval-run ID).
Show child attributes
Show child attributes
{ "agent": "support-bot", "conversation": "c-8891" }
Optional overall judgement: positive, negative, or neutral. Omit to send a comment with no rating.
positive, negative, neutral Who is submitting: user (default) or agent.
user, agent deprecated: use collection
1"sub_tenant_4567"
deprecated: use database
1"tenant_1234"
Response
Created
Show child attributes
Show child attributes
{ "created_at": "2026-07-02T10:00:00Z", "message": "Success", "recorded": true, "request_id": "9d13aef4-02f4-4e73-8c62-4c2601d04f9d" }
Error message, empty string on success.
Show child attributes
Show child attributes
{ "code": "DATABASE_NOT_FOUND", "message": "Database not found" }
Show child attributes
Show child attributes
{ "collection": "team_docs", "database": "acme_corp", "latency_ms": 12.3, "request_id": "9d13aef4-02f4-4e73-8c62-4c2601d04f9d", "source_type": "file", "sub_tenant_id": "sub_tenant_4567", "tenant_id": "tenant_1234" }
Whether the request succeeded.
true
Was this page helpful?
