The trainee app submits videos and marks feedback as seen through its own routes in
modules/trainee/trainee.routes.ts (POST /technique-videos and /technique-videos/:id/feedback-seen under the trainee lane). Those routes are not documented on this page.
Tenancy is enforced through the trainee relation: every query filters on client: { studioId }. The module does not apply coach permission scoping from middleware/coach-access.ts.
The TechniqueVideo row
All endpoints return full Prisma rows with no field selection.
Endpoints
GET /v1/web/technique-videos
Lists the studio’s technique videos, newest first.
Auth: web lane, any role.
string
One of
PENDING, IN_REVIEW, REVIEWED. Omit to return every status.string
Limit the list to one trainee.
integer
default:"1"
Page number, positive.
integer
default:"20"
Rows per page. Positive, maximum 500.
createdAt descending. The findMany and the count run in parallel with the same filter.
Response:
UNAUTHORIZED when the request has no studio context. VALIDATION (422) when the query is invalid, for example an unknown status.
POST /v1/web/technique-videos
Creates a technique video row for a trainee. Returns 201.
Auth: web lane, any role.
string
required
The trainee. Must belong to the caller’s studio.
string
The exercise the clip shows. The service does not check that the id exists.
string
required
Video location. Minimum length 1. Not validated as a URL.
string
Poster image location.
repo.clientInStudio, then inserts the row. The status starts at PENDING from the column default. No activity record, notification or job is created.
Response: the created TechniqueVideo row.
NOT_FOUND (client not found) when clientId is not in the studio. VALIDATION for a bad body. UNAUTHORIZED with no studio context.
GET /v1/web/technique-videos/:id
Returns one technique video.
Auth: web lane, any role.
string
required
The technique video id.
TechniqueVideo row, same shape as the create response.
Errors: NOT_FOUND (technique video not found) when the id does not exist or belongs to another studio.
PATCH /v1/web/technique-videos/:id
Saves a review: status, written note, voice note and reviewer fields. Every body field is optional and only the fields sent are written.
Auth: web lane, any role.
string
required
The technique video id.
string
One of
PENDING, IN_REVIEW, REVIEWED.string
Written feedback for the trainee.
string
Location of a recorded voice note.
string
Id of the reviewer. The caller supplies it. The service does not derive it from
req.auth.date
When the review happened. Parsed with
z.coerce.date(). The caller supplies it. The service does not stamp it.NOT_FOUND, then updates the row.
Side effect: when coachNote is present and not blank after trimming, the service calls repo.recordFeedbackActivity. That inserts a ClientActivity row so the feedback shows in the trainee’s activity feed:
entityName is the note cut to 120 characters. A failure to write the activity is swallowed (.catch(() => null)), so the review still saves. A voice note on its own does not create an activity row. This module sends no push notification.
Response: the updated TechniqueVideo row.
NOT_FOUND (technique video not found), VALIDATION for a bad body, UNAUTHORIZED with no studio context.