Feedback
Feedback records capture human-in-the-loop signals such as thumbs, ratings, comments, and corrections. Use feedback when you need to attach user, QA, Studio, or system review data to a trace or span and query that data alongside other observability signals.
Unlike metrics and scores, feedback is usually supplied by a person or review workflow. Numeric feedback values can be aggregated, grouped, charted over time, and queried for percentiles.
When to use feedbackDirect link to When to use feedback
- Collect user satisfaction ratings for agent responses.
- Store QA comments or corrections next to the trace they review.
- Build dashboards for ratings by agent, environment, or experiment.
Add feedbackDirect link to Add feedback
Use mastra.observability.addFeedback() when you want to annotate a persisted trace or span from app code. The helper is optional on the observability entrypoint, so check that the active observability implementation supports it. See the client SDK observability reference for all client methods.
if (!mastra.observability.addFeedback) {
throw new Error('Feedback is not supported by the active observability implementation')
}
await mastra.observability.addFeedback({
traceId: 'trace-123',
spanId: 'span-456',
feedback: {
feedbackSource: 'user',
feedbackType: 'rating',
value: 1,
comment: 'The answer solved my issue.',
},
})
When you pass only traceId and spanId, addFeedback() rehydrates the target from configured observability storage before emitting the feedback event. Configure MastraStorageExporter when feedback is added after the traced execution has finished. If the trace isn't available in storage, Mastra logs a warning and drops the feedback event.
During a live traced execution, you can pass its correlationContext to emit feedback without rehydrating the trace from storage. This path is useful when the active request collects feedback before its tracing context ends.
Find the trace for a messageDirect link to Find the trace for a message
Feedback is usually collected against a message a user has already read, so you need the traceId for that message. Assistant messages carry it in content.metadata, both in the stream result and when the message is recalled later from memory:
const agent = mastra.getAgent('weatherAgent')
const memory = await agent.getMemory()
const { messages } = await memory!.recall({ threadId, perPage: false })
const message = messages.find(m => m.id === messageId)
const traceId = message?.content.metadata?.traceId
The value is the same trace the run reports as traceId on its result, so feedback collected at generation time and feedback collected later against a stored message anchor to the same trace. Messages produced while tracing is disabled have no traceId.
Create feedbackDirect link to Create feedback
Every createFeedback() requires feedbackType and value. Add traceId or spanId when the feedback should be anchored to a trace or a specific span. Use feedbackSource as optional string metadata, such as user, qa, studio, or system.
For storage-level writes, include timestamp because the method writes directly to the store.
const observability = await mastra.getStorage()!.getStore('observability')
await observability!.createFeedback({
feedback: {
feedbackId: 'feedback-rating-1',
timestamp: new Date(),
traceId: 'trace-123',
spanId: 'span-456',
feedbackSource: 'user',
feedbackType: 'rating',
value: 1,
comment: 'The answer solved my issue.',
tags: ['production'],
},
})
await observability!.createFeedback({
feedback: {
feedbackId: 'feedback-comment-1',
timestamp: new Date(),
feedbackSource: 'qa',
feedbackType: 'comment',
value: 'Needs a citation before shipping.',
experimentId: 'support-agent-eval',
},
})
List feedbackDirect link to List feedback
Use listFeedback() to page through raw records or poll with delta mode.
const result = await observability!.listFeedback({
filters: {
feedbackType: 'rating',
feedbackSource: 'user',
timestamp: { start: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000) },
},
pagination: { page: 0, perPage: 20 },
orderBy: { field: 'timestamp', direction: 'DESC' },
})
console.log(result.feedback, result.pagination?.hasMore)
Filters include target fields such as traceId and spanId, feedback fields such as feedbackType, feedbackSource, and feedbackUserId, and shared context fields such as entityName, environment, experimentId, and tags. See FeedbackFilter for every filter field. Over HTTP, the same fields are passed as query parameters, for example GET /api/observability/feedback?traceId=trace-123&environment=production. See list query parameters.
await observability!.listFeedback({
filters: {
traceId: 'trace-123',
feedbackType: ['rating', 'thumbs'],
tags: ['production'],
},
})
Delete feedbackDirect link to Delete feedback
Use deleteFeedback() to delete feedback records by id. This is useful when a comment contains sensitive data. Each request accepts at most 1,000 feedbackIds, and deletion is idempotent. The optional organizationId field filters by organization, and the optional resourceId field filters by resource. You can supply either filter independently or use both together. In Studio, each comment on a trace or span Feedback tab has a delete action. Deleting a trace with deleteTraces() also deletes the feedback linked to it. See Deleting traces.
await observability!.deleteFeedback({
feedbackIds: ['feedback-1', 'feedback-2'],
})
Deleted records also disappear from feedback analytics. ClickHouse uses a lightweight delete to hide rows without guaranteeing immediate physical removal, so open-source deployments must configure an observability retention period to physically purge them. Each request is marked applied once its delete succeeds; if the delete fails, the request stays unapplied, doesn't block updates to the still-visible feedback, and you retry by calling the delete API again. On ClickHouse, the next review update also retries the delete and reports the feedback as not found if the retry succeeds, or saves the new status and returns the delete's error if it fails again. Open-source deployments have no background reconciler. Configure retention for every observability signal to expire deletion requests after the signal rows they protect. If any signal is unbounded, deletion requests also remain unbounded to prevent deleted data from being reintroduced. On ClickHouse, delete APIs leave the separate delta cursor table untouched. Its rows contain identifiers rather than feedback payloads and expire within two days.
Query feedback analyticsDirect link to Query feedback analytics
OLAP feedback queries operate on numeric value fields. Use them for ratings, thumbs encoded as 1 and -1, numeric QA scores, or other numeric feedback types.
const rating = await observability!.getFeedbackAggregate({
feedbackType: 'rating',
feedbackSource: 'user',
aggregation: 'avg',
comparePeriod: 'previous_day',
})
const byAgent = await observability!.getFeedbackBreakdown({
feedbackType: 'rating',
groupBy: ['entityName'],
aggregation: 'avg',
filters: { environment: 'production' },
})
const ratingsOverTime = await observability!.getFeedbackTimeSeries({
feedbackType: 'rating',
aggregation: 'avg',
interval: '1h',
groupBy: ['feedbackSource'],
})
See the feedback reference for all fields, filters, return types, and percentile query parameters.
The local runtime exposes the list route at /api/observability/feedback and analytics under its related paths. See the HTTP routes table and list query parameters. Mastra Platform provides a separate, unversioned hosted query API. See the Feedback API for regional endpoints, authentication, and project scoping.
Export feedback to Mastra PlatformDirect link to Export feedback to Mastra Platform
MastraPlatformExporter forwards emitted feedback events to Mastra Platform automatically because the hosted query API doesn't provide a creation route.
If your application adds feedback after an agent or workflow response using only its traceId, configure MastraStorageExporter alongside MastraPlatformExporter. The storage exporter keeps the trace available for addFeedback() to rehydrate, while the Platform exporter forwards the resulting feedback event without adding the trace to the application's local storage.
See Observability on Mastra Platform for the combined exporter configuration.
Export feedback to external platformsDirect link to Export feedback to external platforms
Feedback flows through the observability event bus, so exporters that support feedback forward it automatically. The PostHog exporter sends feedback as native $ai_feedback events that appear on the linked trace in PostHog.