Skip to main content

Feedback

Added in: @mastra/core@1.18.0

Feedback APIs store and query human-in-the-loop observability signals such as ratings, thumbs, comments, and corrections. Use the feedback guide for usage patterns.

Usage example
Direct link to Usage example

The following example records a rating for a persisted trace through the observability entrypoint. addFeedback() is optional on the entrypoint, so check that the active observability implementation supports it before calling it.

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: 'Helpful answer.',
},
})

Create feedback
Direct link to Create feedback

addFeedback(args)
Direct link to addfeedbackargs

Adds feedback to a persisted trace or span through the observability entrypoint.

await mastra.observability.addFeedback?.({
traceId: 'trace-123',
spanId: 'span-456',
feedback: {
feedbackSource: 'user',
feedbackType: 'rating',
value: 1,
comment: 'Helpful answer.',
},
})

traceId?:

string
Trace that anchors the feedback target.

spanId?:

string
Span that anchors the feedback target.

correlationContext?:

CorrelationContext
Live span or trace context to emit from without rehydrating the target from storage.

feedback:

FeedbackInput
Feedback payload to add.

Without correlationContext, the method looks up traceId in configured observability storage. If the trace or requested span isn't found, Mastra logs a warning and drops the feedback event. Configure MastraStorageExporter when adding feedback by ID after execution, including when MastraPlatformExporter handles remote export.

createFeedback(args)
Direct link to createfeedbackargs

Creates one feedback record through the observability storage domain. Storage-level calls write directly to the store, so include timestamp.

await observability.createFeedback({
feedback: {
feedbackId: 'feedback-1',
timestamp: new Date(),
traceId: 'trace-123',
spanId: 'span-456',
feedbackSource: 'user',
feedbackType: 'rating',
value: 1,
comment: 'Helpful answer.',
},
})

The HTTP and client SDK create route accepts CreateFeedbackBody and sets timestamp server-side. It generates feedbackId when omitted:

await mastraClient.createFeedback({
feedback: {
traceId: 'trace-123',
spanId: 'span-456',
feedbackSource: 'user',
feedbackType: 'rating',
value: 1,
},
})

batchCreateFeedback(args)
Direct link to batchcreatefeedbackargs

Creates multiple feedback records through the observability storage domain. This method isn't exposed by the HTTP routes or @mastra/client-js.

await observability.batchCreateFeedback({
feedbacks: [
{
feedbackId: 'feedback-1',
timestamp: new Date(),
traceId: 'trace-123',
feedbackSource: 'user',
feedbackType: 'rating',
value: 1,
},
{
feedbackId: 'feedback-2',
timestamp: new Date(),
traceId: 'trace-123',
feedbackSource: 'qa',
feedbackType: 'comment',
value: 'Needs a citation before shipping.',
},
],
})

Delete feedback
Direct link to Delete feedback

deleteFeedback(args)
Direct link to deletefeedbackargs

Deletes up to 1,000 feedback records by id. The operation is idempotent: ids that don't exist are ignored, and an empty feedbackIds array is a no-op. When organizationId or resourceId is provided, only records matching that scope are deleted.

await mastraClient.deleteFeedback({
feedbackIds: ['feedback-1', 'feedback-2'],
})

feedbackIds:

string[]
Ids of the feedback records to delete. Accepts at most 1,000 ids.

organizationId?:

string
Restricts the delete to records with this organization id.

resourceId?:

string
Restricts the delete to records with this resource id.

Returns Promise<{ success: boolean }> from mastraClient.deleteFeedback() and the HTTP route. The storage domain method returns Promise<void>. Requests with more than 1,000 ids return 400, and a server running @mastra/core older than 1.66.0 returns 501. ClickHouse vNext, PostgreSQL vNext, DuckDB, and the in-memory store implement deletion. Every other adapter, including LibSQL and MongoDB, throws OBSERVABILITY_STORAGE_DELETE_FEEDBACK_NOT_IMPLEMENTED.

On ClickHouse, deletion uses lightweight deletes on the main feedback events table to remove rows from reads, including OLAP queries, 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 updateFeedbackReviewStatus() on the still-visible feedback, and you retry by calling deleteFeedback() again. updateFeedbackReviewStatus() also retries the delete and throws a not-found error if the retry succeeds, or saves the new status and throws the delete's error if it fails again. Open-source deployments have no background reconciler. ClickHouse applies a TTL to deletion requests only when all five signals have finite retention. See ClickHouse native TTL. Delete APIs leave the separate delta cursor table untouched. Its rows contain identifiers rather than feedback payloads and expire within two days.

deleteScores(args)
Direct link to deletescoresargs

Deletes up to 1,000 score records by id. It behaves like deleteFeedback() with scoreIds in place of feedbackIds, and Oracle Database also implements it. Unsupported adapters throw OBSERVABILITY_STORAGE_DELETE_SCORES_NOT_IMPLEMENTED.

await mastraClient.deleteScores({
scoreIds: ['score-1', 'score-2'],
})

List feedback
Direct link to List feedback

listFeedback(args?)
Direct link to listfeedbackargs

Returns feedback records in page mode or delta mode.

const response = await mastraClient.listFeedback({
filters: {
feedbackType: 'rating',
feedbackSource: 'studio',
},
pagination: { page: 0, perPage: 20 },
orderBy: { field: 'timestamp', direction: 'DESC' },
})

mode?:

'page' | 'delta'
List mode. Defaults to 'page'.

filters?:

FeedbackFilter
Filters for the feedback records.

orderBy?:

{ field?: 'timestamp'; direction?: 'ASC' | 'DESC' }
Page-mode sort configuration.

after?:

string
Delta cursor for incremental polling. Only valid in delta mode.

limit?:

number
Maximum number of updates to return in delta mode.

OLAP queries
Direct link to OLAP queries

OLAP feedback queries operate on numeric value fields.

getFeedbackAggregate(args)
Direct link to getfeedbackaggregateargs

Returns one aggregate feedback value.

const response = await mastraClient.getFeedbackAggregate({
feedbackType: 'rating',
feedbackSource: 'user',
aggregation: 'avg',
comparePeriod: 'previous_day',
})

feedbackType:

string
Feedback type to aggregate.

feedbackSource?:

string
Feedback source to aggregate.

aggregation:

'sum' | 'avg' | 'min' | 'max' | 'count' | 'count_distinct' | 'last'
Aggregation to apply.

filters?:

FeedbackFilter
Additional filters.

comparePeriod?:

'previous_period' | 'previous_day' | 'previous_week'
Optional period-over-period comparison.

getFeedbackBreakdown(args)
Direct link to getfeedbackbreakdownargs

Returns feedback values grouped by dimensions.

const response = await mastraClient.getFeedbackBreakdown({
feedbackType: 'rating',
groupBy: ['entityName'],
aggregation: 'avg',
})

getFeedbackTimeSeries(args)
Direct link to getfeedbacktimeseriesargs

Returns feedback values bucketed by interval.

const response = await mastraClient.getFeedbackTimeSeries({
feedbackType: 'rating',
interval: '1h',
aggregation: 'avg',
groupBy: ['feedbackSource'],
})

getFeedbackPercentiles(args)
Direct link to getfeedbackpercentilesargs

Returns percentile values bucketed by interval.

const response = await mastraClient.getFeedbackPercentiles({
feedbackType: 'rating',
percentiles: [0.5, 0.95],
interval: '1d',
})

Types
Direct link to Types

FeedbackRecord
Direct link to feedbackrecord

feedbackId?:

string | null
Unique ID for this feedback event. The server route generates one when omitted.

timestamp:

Date
Time when the feedback was recorded.

traceId?:

string | null
Trace that anchors the feedback target when available.

spanId?:

string | null
Span that anchors the feedback target when available.

feedbackSource?:

string | null
Optional source metadata, such as 'user', 'qa', 'studio', or 'system'.

source?:

string | null
Deprecated alias for feedbackSource.

feedbackType:

string
Type of feedback, such as 'rating', 'thumbs', 'comment', or 'correction'.

value:

number | string
Feedback value. Numeric values support aggregate, breakdown, time series, and percentile queries.

comment?:

string | null
Additional comment or context for the feedback.

feedbackUserId?:

string | null
User who provided the feedback.

sourceId?:

string | null
ID of the source record this feedback is linked to, such as an experiment result ID.

metadata?:

Record<string, unknown> | null
User-defined metadata for the feedback record.

Shared context fields
Direct link to Shared context fields

Feedback records can include shared observability context fields for filtering, grouping, and correlation with traces, logs, metrics, and scores.

entityType?:

EntityType | null
Entity type that produced the signal.

entityId?:

string | null
ID of the entity that produced the signal.

entityName?:

string | null
Name of the entity that produced the signal.

parentEntityType?:

EntityType | null
Entity type of the parent entity.

parentEntityId?:

string | null
ID of the parent entity.

parentEntityName?:

string | null
Name of the parent entity.

rootEntityType?:

EntityType | null
Entity type of the root entity.

rootEntityId?:

string | null
ID of the root entity.

rootEntityName?:

string | null
Name of the root entity.

userId?:

string | null
Human end user who triggered execution.

organizationId?:

string | null
Multi-tenant organization or account.

resourceId?:

string | null
Broader resource context.

runId?:

string | null
Execution run identifier.

sessionId?:

string | null
Session identifier for grouping traces.

threadId?:

string | null
Conversation thread identifier.

requestId?:

string | null
HTTP request ID for correlation.

environment?:

string | null
Deployment environment.

serviceName?:

string | null
Name of the service.

scope?:

Record<string, unknown> | null
Package, app version, or deployment metadata.

entityVersionId?:

string | null
Version ID of the entity that produced the signal.

parentEntityVersionId?:

string | null
Version ID of the parent entity.

rootEntityVersionId?:

string | null
Version ID of the root entity.

experimentId?:

string | null
Experiment or eval run identifier.

executionSource?:

string | null
Source of execution, such as local, cloud, or CI.

tags?:

string[] | null
Labels for filtering.

FeedbackInput
Direct link to feedbackinput

Use FeedbackInput with mastra.observability.addFeedback(), recordedTrace.addFeedback(), and recordedSpan.addFeedback().

feedbackSource?:

string
Optional source metadata for the feedback.

source?:

string
Deprecated alias for feedbackSource.

feedbackType:

string
Type of feedback to record.

value:

number | string
Feedback value to record.

comment?:

string
Additional comment or context.

feedbackUserId?:

string
User who provided the feedback.

userId?:

string
Deprecated alias for feedbackUserId.

metadata?:

Record<string, unknown>
Additional feedback-specific metadata.

experimentId?:

string
Experiment or eval run identifier.

sourceId?:

string
ID of the source record this feedback is linked to.

FeedbackFilter
Direct link to feedbackfilter

Use FeedbackFilter in listFeedback() and OLAP query filters.

timestamp?:

{ start?: Date; end?: Date; startExclusive?: boolean; endExclusive?: boolean }
Filter by timestamp range.

traceId?:

string
Filter by trace ID.

spanId?:

string
Filter by span ID.

feedbackType?:

string | string[]
Filter by one or more feedback types.

feedbackSource?:

string
Filter by feedback source.

source?:

string
Deprecated alias for feedbackSource.

feedbackUserId?:

string
Filter by the user who provided the feedback.

reviewStatus?:

'needs-review' | 'reviewed'
Filter by review status.

entityType?:

EntityType
Filter by entity type.

entityName?:

string
Filter by entity name.

entityVersionId?:

string
Filter by entity version ID.

parentEntityType?:

EntityType
Filter by parent entity type.

parentEntityName?:

string
Filter by parent entity name.

parentEntityVersionId?:

string
Filter by parent entity version ID.

rootEntityType?:

EntityType
Filter by root entity type.

rootEntityName?:

string
Filter by root entity name.

rootEntityVersionId?:

string
Filter by root entity version ID.

userId?:

string
Filter by human end-user ID.

organizationId?:

string
Filter by organization ID.

resourceId?:

string
Filter by resource ID.

runId?:

string
Filter by run ID.

sessionId?:

string
Filter by session ID.

threadId?:

string
Filter by thread ID.

requestId?:

string
Filter by request ID.

serviceName?:

string
Filter by service name.

environment?:

string
Filter by environment.

executionSource?:

string
Filter by execution source.

experimentId?:

string
Filter by experiment or eval run identifier.

tags?:

string[]
Filter by tags. Matching records must have all specified tags.

HTTP routes
Direct link to HTTP routes

These routes belong to a Mastra runtime and use its configured observability storage. They're separate from the unversioned Mastra Platform Feedback API, which doesn't provide a feedback creation route.

MethodPathPurposePermission
GET/api/observability/feedbackList feedback recordsobservability:read
POST/api/observability/feedbackCreate a feedback recordobservability:write
DELETE/api/observability/feedbackDelete feedback records by idobservability:delete
DELETE/api/observability/scoresDelete score records by idobservability:delete
PATCH/api/observability/feedback/:feedbackId/review-statusUpdate review statusobservability:write
POST/api/observability/feedback/aggregateReturn one aggregate valueobservability:read
POST/api/observability/feedback/breakdownGroup feedback by dimensionsobservability:read
POST/api/observability/feedback/timeseriesBucket feedback by intervalobservability:read
POST/api/observability/feedback/percentilesReturn percentile seriesobservability:read

List query parameters
Direct link to List query parameters

GET /api/observability/feedback takes its arguments as URL query parameters. The Mastra Platform Feedback API accepts the same parameters on its GET /feedback endpoint.

curl -sS "http://localhost:4111/api/observability/feedback?traceId=trace-123&environment=production&feedbackType=rating&page=0&perPage=20"

Every field of FeedbackFilter is accepted as a top-level query parameter with the same name, for example traceId, spanId, feedbackType, feedbackSource, feedbackUserId, reviewStatus, entityName, environment, experimentId, or tags. Repeat a parameter to pass multiple values where the filter accepts an array, such as feedbackType=rating&feedbackType=thumbs. Pass object-valued filters such as timestamp as JSON, for example timestamp={"start":"2026-01-01T00:00:00Z"} URL-encoded.

The remaining parameters control paging and delta polling:

mode?:

'page' | 'delta'
List mode. Defaults to 'page'.

page?:

number
= 0
Zero-indexed page number. Page mode only.

perPage?:

number
= 10
Records per page, from 1 to 100. Page mode only.

field?:

'timestamp'
= 'timestamp'
Sort field. Page mode only.

direction?:

'ASC' | 'DESC'
= 'DESC'
Sort direction. Page mode only.

after?:

string
Opaque delta cursor returned by the previous delta response. Delta mode only.

limit?:

number
Maximum number of updates to return, from 1 to 100. Delta mode only.

Requests that mix modes return 400, for example page or perPage with mode=delta, or after or limit without it. The analytics routes take the same JSON bodies as getFeedbackAggregate(), getFeedbackBreakdown(), getFeedbackTimeSeries(), and getFeedbackPercentiles().