These endpoints cover the trainee’s own account and the home screen. Most live on the main trainee router. The last three live in the notification configs module. Mount: /v1/trainee, router traineeRouter in src/modules/trainee/trainee.routes.ts. Also /v1/trainee/notification-configs (notificationConfigsReadRouter) and /v1/trainee/notification-preferences (notificationPreferencesRouter) in src/modules/notification-configs/notification-configs.routes.ts. Lane: trainee. Every endpoint needs a trainee token. The table shows which also use the app access guard (access) that returns 403 APP_LOCKED for a frozen or ended subscription. See Authentication.

Account

GET /v1/trainee/me

Returns the signed-in trainee, their studio and every studio the same phone number trains at. The app calls it on launch to refresh branding, subscription state and the lock flag. Auth: trainee token.
string
ios or android. Saved as Client.lastCheckInPlatform when the check-in is stamped.
Side effects, skipped for preview tokens: Client.lastCheckInAt is updated at most once every 15 minutes (LAST_CHECK_IN_THROTTLE_MS). When it is updated, open automatic INACTIVE inbox items for the trainee are marked DONE and a ClientAppDay row is upserted for today in the trainee’s time zone. The write is fire and forget and never fails the request. Response: 200.
The client and studio objects are the same views returned by sign-in. Field notes are on Trainee sign-in. studio.branding is the studio’s stored branding JSON with the Instagram handle normalized. mbpEnabled, mbpAnchors and mbpUnitLabel describe the studio’s portion system and are read from Studio.settings. The numbers above are placeholders. Errors:

DELETE /v1/trainee/me

Deletes the trainee’s account from inside the app. This is the store-mandated account deletion. Auth: trainee token. Works for a locked account. Refused for preview tokens. repo.deleteTraineeAccount runs one transaction that:
  • deletes the trainee’s progress photos, technique videos, health samples, meal logs, nutrition day logs, favorite meals, shopping list, form responses, form assignments, inbox items, push tokens and WhatsApp messages,
  • detaches calendar events (clientId set to null),
  • anonymizes the Client row: deletedAt set, status ARCHIVED, the name replaced with a fixed Hebrew placeholder, and name parts, email, phone, avatar, goal, id number, birth date, age, gender, height, weight, tags and coach cleared. planFrozenOn is set and appAccessWhileFrozen is false.
externalUserId is kept only when it starts with autofit:, so a later AutoFit import still recognizes the row. Workout logs, weight entries and daily metrics are not in the delete list. Response: 200.
Errors:

POST /v1/trainee/me/avatar

Sets the trainee’s profile photo. Upload the image first with Trainee uploads, then send the returned URL here. Auth: trainee token and app access.
string
required
A valid URL, at most 1000 characters.
Response: 200 with { client, studio }. Same views as GET /v1/trainee/me, without studios. The update query does not load subscriptions, so client.subscription.status is none in this response regardless of the real state. Refetch me if you need it. Errors:

App release

GET /v1/trainee/app-release

Tells the app the newest version live in its store, so it can prompt for an update. Auth: trainee token.
string
ios or android. Without it the response is all null.
Values come from env vars, not the database: TRAINEE_APP_IOS_VERSION, TRAINEE_APP_IOS_STORE_URL, TRAINEE_APP_ANDROID_VERSION, TRAINEE_APP_ANDROID_STORE_URL. The version must match ^\d+(\.\d+){0,3}$ and the store URL must be non-empty, otherwise both fields are null, which turns the prompt off. The comparison with the installed version happens in the app. Response: 200.
Errors: none beyond the lane errors.

Push tokens

POST /v1/trainee/push-token

Registers the device’s push token for this trainee. Auth: trainee token.
string
required
The device push token. 1 to 300 characters.
string
ios or android.
The unique key on TraineePushToken is (clientId, token). In one transaction the repository first deletes rows with the same token that belong to other clients, then upserts this client’s row. That is what stops a handed-over phone from receiving the previous user’s notifications. After the upsert, notifyPendingForms runs for the trainee: forms that were assigned before the device could receive a push are announced now, and the FORM_SENT task hook fires for each one that became visible. A failure there is logged and does not fail the request. Response: 200.
Errors: VALIDATION (422) when the body fails the schema.

DELETE /v1/trainee/push-token

Removes the device token on sign-out. Auth: trainee token.
string
required
The token to remove. Sent in the JSON body of the DELETE request.
Only the caller’s own row is deleted. If the token has already moved to another account on the same phone, that row is left alone. Response: 204, no body. Deleting a token that is not registered is also 204. Errors: VALIDATION (422) when token is missing.

Home

GET /v1/trainee/home

Everything the home tab renders: progress rings, today’s workout card, the next meal, the studio banner, the WhatsApp contact, weekly counters and the movement requirement. Auth: trainee token and app access.
string
1 to 128 characters. The nutrition plan the app currently shows as today’s. A hint: used when it matches one of the trainee’s active plans.
string
1 to 128 characters. The plan day the app currently shows as today’s. Used together with planId. When the hint does not resolve, the server infers the day from today’s saved snapshot and then from the plan’s day rotation.
string
ios or android. Used for the check-in stamp.
How the payload is built:
  • Check-in. Same throttled lastCheckInAt stamp as GET /v1/trainee/me, skipped for preview tokens.
  • Today. The day window is computed in the trainee’s time zone.
  • Nutrition. All active nutrition programs are loaded. If the newest one is a file plan (a PDF or link), nutrition is in file-plan mode: no macro rings, no next meal, mealsToday is null. Otherwise the day is picked from the query hint or inferred, then summarized by summarizePlanDay, which mirrors the app’s own math over the saved day snapshot: only the option the trainee has selected counts, trainee-set portions replace plan quantities, and excluded foods are replaced the same way Trainee nutrition serves them. Free-logged meals from MealLog are added on top, except logs already linked to a snapshot meal.
  • Rings. With the portion system off, four macro rings in kcal and grams. With it on, the same four keys carry portion units and the label is the studio’s unit label. Then water in litres (goal from the plan day’s water target, else Client.waterGoalMl) and steps (goal from the training plan’s requirement for the next workout day, else Client.stepsGoal).
  • Workout. The newest active training program. For a regular plan the card is the next day in rotation: the day after the last performed one, by label. completed is true when any completed workout log exists today. For a file plan the card opens the file and estimatedMinutes is null.
  • Next meal. The first plan meal whose time is at or after the current local time and that is neither ticked off nor removed for today. If none is later, the first remaining meal. calories is kcal, or portion units rounded to quarters when the portion system is on. items holds up to 4 food names.
  • Banner. The studio’s published home banner, resolved by resolveTraineeBanner.
  • WhatsApp. coachWhatsapp is null when the studio set settings.contactWhatsappEnabled to false.
Response: 200.
array
Ordered list. Macro rings are omitted in nutrition file-plan mode, so the array then holds only water and steps. Labels are Hebrew strings from the server.
object | null
null when the trainee has no active training program. kind is regular or file. For regular, id is the plan day id. For file, id is the program id.
object | null
The latest outbound WhatsappMessage with a body. coachName is the studio name and unread is always false.
object | null
May carry target when the plan meal defines a target the trainee composes against.
object | null
null when there is no published banner or it has no title. mediaKind is image, video, youtube or null. cta.kind is content, link or whatsapp, and cta is null when the banner has no action.
object
completed counts distinct days with a completed log in the last 7 days. total is the number of days in the plan, or the coach’s workoutsPerWeek for a file plan, which can be null.
object | null
null in nutrition file-plan mode.
object | null
null when the training plan has no cardio or steps requirement switched on. mode is cardioOnly, stepsOnly, both or bothCompensated. cardio.scope is perWorkout or weekly. The same block is returned by GET /v1/trainee/workouts. See Plans and workouts.
Errors:

Notification settings

Push notification behaviour has two layers. The studio configures each notification type (on or off, texts, sound, send time). The trainee can then mute whole groups. Both layers are read by the app.

GET /v1/trainee/notification-configs

Returns the studio’s notification configuration as it applies to this trainee. The app uses it for notifications it schedules on the device, such as workout reminders. Auth: trainee token. The service lists the studio’s effective config per type and sets enabled to false for every type whose group the trainee muted. The COACH_MESSAGES group can never be muted. Response: 200. data is an array.
type is one of the values in notificationConfigType. The active set is REST_TIMER, WORKOUT_IDLE, WORKOUT_UNFINISHED, FORM_SENT, FORM_REMINDERS, TRAINING_PLAN_ASSIGNED, TRAINING_PLAN_UPDATED, NUTRITION_PLAN_ASSIGNED, NUTRITION_PLAN_UPDATED, SUBSCRIPTION_RENEWED, SUBSCRIPTION_FROZEN and TRAINER_MESSAGE. The texts above are placeholders. Real templates come from the defaults and the studio’s edits. Errors: NOT_FOUND (404) trainee not found when the client is not in the token’s studio.

GET /v1/trainee/notification-preferences

Lists the notification groups the trainee can switch on and off. Auth: trainee token. A group is listed only when the studio has at least one enabled notification type in it. Groups come from NOTIFICATION_TYPE_GROUPS. For FORM_SENT and FORM_REMINDERS the group depends on each enabled message’s form type: CHECK_IN messages belong to CHECK_INS, others to FORMS, and a message for all form types belongs to both. Response: 200.
string
CHECK_INS, FORMS, PLANS, WORKOUT_REMINDERS, COACH_MESSAGES or SUBSCRIPTION, in that order.
boolean
true for COACH_MESSAGES. A locked group is always enabled.
boolean
false when the group is in Client.mutedNotificationGroups.
Errors: NOT_FOUND (404) trainee not found.

PATCH /v1/trainee/notification-preferences

Switches one group on or off. Auth: trainee token. Refused for preview tokens.
string
required
One of the six group keys.
boolean
required
false adds the group to the trainee’s muted list. true removes it.
The muted list is stored on the client row. Server-side sends check it through isNotificationMuted, and the next GET /v1/trainee/notification-configs reflects it for device-local notifications. Response: 200 with the same { groups } shape as the GET, after the change. Errors: