Guides
Admin Feedback Management
Query feedback lists, inspect sentiment metrics, retrieve audio playback URLs, and trigger manual reprocessing.
Query feedback lists, inspect sentiment metrics, retrieve audio playback URLs, and trigger manual reprocessing for Voiceflow recordings.
All admin endpoints are routed through adminRouter mounted under /api/admin and require authenticated requests with an admin role, enforced by requireUser and requireAdmin.
Listing and Filtering Feedback#
Admins can list feedback across all users with cursor-based pagination. Use listFeedback by sending a GET request to /api/admin/feedback.
Query Parameters#
You can provide the following query parameters:
cursor: The string ID of the last feedback item from the previous page.status: Filter by feedback status (UPLOADING,UPLOADED,PROCESSING,COMPLETED,FAILED,EXPIRED).sentiment: Filter by analysis sentiment label.language: Filter by the transcription language code.from: Start timestamp to filter thecreatedAtdate.to: End timestamp to filter thecreatedAtdate.
Response#
The endpoint returns up to 20 items per page ordered newest first (createdAt: "desc", id: "desc"), along with a nextCursor value:
{
"items": [
{
"id": "cm1abc...",
"status": "COMPLETED",
"errorCode": null,
"durationMs": 14200,
"createdAt": "2025-01-15T10:00:00.000Z",
"user": {
"name": "Alex Doe",
"email": "alex@example.com"
},
"transcription": {
"englishText": "The app is very intuitive and fast.",
"languageCode": "en-IN"
},
"analysis": {
"label": "positive",
"score": 0.9,
"urgent": false
}
}
],
"nextCursor": "cm1xyz..."
}
If no additional pages exist, nextCursor returns null.
Inspecting Feedback Details#
To inspect the full details of a specific submission, use getFeedback by calling GET /api/admin/feedback/:id.
This endpoint returns the feedback record excluding the raw internal audioPath, and includes the related user (name and email), full transcription record, and sentiment analysis.
If the ID does not match any record, the server returns an HTTP 404 error:
{
"error": {
"code": "NOT_FOUND",
"message": "Recording not found"
}
}
Audio Playback URLs#
To listen to an uploaded audio recording, call getAudioUrl using GET /api/admin/feedback/:id/audio.
The endpoint generates a short-lived signed S3 playback URL:
{
"url": "https://...",
"expiresIn": 60
}
The signed URL is valid for 60 seconds.
If the audio retention period has elapsed, if audioDeletedAt is set, or if the audio object no longer exists in storage, this endpoint returns a 404 response with code AUDIO_REMOVED:
{
"error": {
"code": "AUDIO_REMOVED",
"message": "Audio has been removed"
}
}
Reprocessing Feedback#
When a recording pipeline has completed or failed, admins can re-run processing via reprocess with a POST request to /api/admin/feedback/:id/reprocess.
- The service checks that the recording is currently in either
FAILEDorCOMPLETEDstatus. - It resets the status to
UPLOADED, increments theattemptcounter, and clearserrorCodeandcompletedAt. - It emits a new Inngest event named
feedback/uploadedkeyed by${id}-${attempt}so the queue processes the new attempt.
Upon success, the server responds with status 202 Accepted:
{
"status": "UPLOADED"
}
Reprocessing Errors#
- 409 INVALID_STATE: The recording is not in
FAILEDorCOMPLETEDstatus (for example, if it is currentlyPROCESSINGorUPLOADING). - 404 NOT_FOUND: No feedback record exists with the provided ID.
- 502 QUEUE_UNAVAILABLE: The Inngest event could not be dispatched to the queue.
Feedback Statistics#
Admins can retrieve aggregate operational and sentiment metrics by calling getStats with GET /api/admin/stats.
The endpoint returns aggregated counts across feedback and analysis records:
{
"total": 120,
"urgent": 8,
"byStatus": [
{ "status": "COMPLETED", "count": 105 },
{ "status": "FAILED", "count": 15 }
],
"bySentiment": [
{ "label": "positive", "count": 70 },
{ "label": "negative", "count": 35 }
],
"byLanguage": [
{ "language": "en-IN", "count": 80 },
{ "language": "hi-IN", "count": 25 }
],
"volume": [
{ "day": "2025-01-01T00:00:00.000Z", "count": 4 }
],
"topics": [
{ "topic": "pricing", "count": 14 },
{ "topic": "checkout", "count": 9 }
]
}
urgent: Total count of analysis entries flagged asurgent: true.byStatus: Counts grouped by submission status.bySentiment: Counts grouped by sentiment label.byLanguage: Counts grouped by transcription language code (defaults to"unknown"if missing).volume: Daily counts of submitted feedback over the last 30 days.topics: The top 8 most frequent topics extracted from sentiment analyses.