{"components":{"schemas":{"AgentConnectionDto":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":["string","null"]},"tier":{"$ref":"#/components/schemas/AgentScopeTier"},"lastUsedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"revokedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","label","tier","lastUsedAt","revokedAt","createdAt"],"additionalProperties":false,"description":"Safe view of an agent connection. NEVER includes keyHash. The raw key is only present on the create response (CreatedAgentConnectionDto.rawKey), shown once."},"AgentScopeTier":{"type":"string","enum":["read","write","send"],"description":"Per-key SCOPE tiers for the MCP tool surface.\n\nEach agent key carries a scope tier; the MCP bridge filters which tools the key may LIST and CALL. Tiers are cumulative (a `send` key can do everything `write` and `read` can):\n\n  read  → find/get/review style tools — never mutate anything.   write → the above PLUS build/manage tools. Writes commit directly to the live DB           (no staging/apply step — see tool-bridge.ts). No destructive deletes are served.   send  → the above PLUS verbs carrying an outward client-comms path (e.g. reminders).\n\nSECURITY-CRITICAL (fail closed): every served tool MUST appear in TOOL_SCOPES. tool-bridge.ts throws if a served tool is unclassified, so an unclassified tool is never exposed. `send` is opt-in: the highest tier, never the default.\n\n2026-06-24: the served surface was collapsed from 136 legacy CRUD tools to the 18 INTENT verbs (apps/api/src/mcp/intent). The legacy tools survive as the internal composition layer the intent verbs delegate to (intent/dispatch.ts) — they are no longer served, so no longer classified here. See docs/superpowers/specs/2026-06-24-ai-native-intent-surface-design.md."},"AiTranslationResponse":{"type":"object","properties":{"translations":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"index":{"type":"number"},"translation":{"type":"string"}},"additionalProperties":false}},{"type":"null"}]}},"additionalProperties":false,"description":"Mirrors AiTranslationResponse — the JSON shape the model is asked to return."},"AiUsageDto":{"type":"object","properties":{"aiCreditsUsed":{"type":"number"}},"required":["aiCreditsUsed"],"additionalProperties":false,"description":"Mirrors AiUsageDto."},"AnalyzeMealDto":{"type":"object","properties":{"text":{"type":["string","null"]},"media":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]}},"additionalProperties":false,"description":"Ports com.protocolcrm.api.dto.nutrition.NutritionTrackingDto types.\n\nCONTRACT: none of the tracking DTOs carry"},"MediaDto":{"$ref":"#/components/schemas/Record<string,unknown>","description":"MediaDto is a cross-domain blob; passed through verbatim as an opaque object."},"Record<string,unknown>":{"type":"object","additionalProperties":{}},"ApiKeyDto":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"name":{"type":"string"},"apiKeyTemp":{"type":"string"}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.ApiKeyDto (@JsonInclude(NON_NULL)). `apiKeyTemp` carries the raw key value and is only populated in the create response."},"ApiKeyUpsertDto":{"type":"object","properties":{"name":{"type":"string"},"apiKeyTemp":{"type":"string"}},"additionalProperties":false,"description":"Accepted body for create/update (only `name` is persisted)."},"AppAuthentication":{"type":"object","properties":{"disableRegistration":{"type":["boolean","null"]},"disableRegistrationMessage":{"type":["string","null"]},"emailAuthEnabled":{"type":["boolean","null"]},"smsAuthEnabled":{"type":["boolean","null"]},"googleAuthEnabled":{"type":["boolean","null"]},"appleAuthEnabled":{"type":["boolean","null"]},"facebookAuthEnabled":{"type":["boolean","null"]},"requireEmailVerification":{"type":["boolean","null"]},"allowGuestAccess":{"type":["boolean","null"]}},"additionalProperties":false},"AppBranding":{"type":"object","properties":{"logoMedia":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"appIconMedia":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"splashScreenMedia":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"faviconMedia":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]}},"additionalProperties":false},"AppConfigurationDto":{"type":"object","properties":{"id":{"type":"string"},"reviewClientId":{"type":"string"},"general":{"$ref":"#/components/schemas/AppGeneralSettings"},"branding":{"$ref":"#/components/schemas/AppBranding"},"styling":{"$ref":"#/components/schemas/AppThemeConfig"},"features":{"$ref":"#/components/schemas/AppFeatures"},"navigation":{"$ref":"#/components/schemas/AppNavigation"},"notifications":{"$ref":"#/components/schemas/AppNotifications"},"content":{"$ref":"#/components/schemas/AppContent"},"authentication":{"$ref":"#/components/schemas/AppAuthentication"},"appStore":{"$ref":"#/components/schemas/AppStoreSettings"},"storeConfig":{"anyOf":[{"$ref":"#/components/schemas/StoreConfiguration"},{"type":"null"}]}},"additionalProperties":false},"AppGeneralSettings":{"type":"object","properties":{"appName":{"type":["string","null"]},"tagline":{"type":["string","null"]},"description":{"type":["string","null"]},"contactEmail":{"type":["string","null"]},"contactPhone":{"type":["string","null"]},"websiteUrl":{"type":["string","null"]},"supportUrl":{"type":["string","null"]}},"additionalProperties":false},"AppThemeConfig":{"type":"object","properties":{"themeMode":{"anyOf":[{"$ref":"#/components/schemas/ThemeMode"},{"type":"null"}]},"lightTheme":{"anyOf":[{"$ref":"#/components/schemas/AppThemeVariant"},{"type":"null"}]},"darkTheme":{"anyOf":[{"$ref":"#/components/schemas/AppThemeVariant"},{"type":"null"}]}},"additionalProperties":false},"ThemeMode":{"type":"string","enum":["LIGHT","DARK","AUTOMATIC"]},"AppThemeVariant":{"type":"object","properties":{"preset":{"type":["string","null"]},"palette":{"anyOf":[{"$ref":"#/components/schemas/AppPalette"},{"type":"null"}]},"typography":{"anyOf":[{"$ref":"#/components/schemas/AppTypography"},{"type":"null"}]},"shape":{"anyOf":[{"$ref":"#/components/schemas/AppShape"},{"type":"null"}]}},"additionalProperties":false},"AppPalette":{"type":"object","properties":{"primary":{"type":["string","null"]},"secondary":{"type":["string","null"]},"background":{"type":["string","null"]},"surface":{"type":["string","null"]},"textPrimary":{"type":["string","null"]},"textSecondary":{"type":["string","null"]},"accent":{"type":["string","null"]},"error":{"type":["string","null"]},"success":{"type":["string","null"]},"warning":{"type":["string","null"]}},"additionalProperties":false},"AppTypography":{"type":"object","properties":{"fontFamily":{"type":["string","null"]},"heading":{"anyOf":[{"$ref":"#/components/schemas/AppTypographyStyle"},{"type":"null"}]},"heading2":{"anyOf":[{"$ref":"#/components/schemas/AppTypographyStyle"},{"type":"null"}]},"heading3":{"anyOf":[{"$ref":"#/components/schemas/AppTypographyStyle"},{"type":"null"}]},"body":{"anyOf":[{"$ref":"#/components/schemas/AppTypographyStyle"},{"type":"null"}]},"button":{"anyOf":[{"$ref":"#/components/schemas/AppTypographyStyle"},{"type":"null"}]},"caption":{"anyOf":[{"$ref":"#/components/schemas/AppTypographyStyle"},{"type":"null"}]},"label":{"anyOf":[{"$ref":"#/components/schemas/AppTypographyStyle"},{"type":"null"}]}},"additionalProperties":false},"AppTypographyStyle":{"type":"object","properties":{"fontSize":{"type":["string","null"]},"fontWeight":{"type":["string","null"]},"lineHeight":{"type":["string","null"]},"letterSpacing":{"type":["string","null"]}},"additionalProperties":false},"AppShape":{"type":"object","properties":{"borderRadius":{"type":["number","null"]},"cardElevation":{"type":["number","null"]},"spacing":{"type":["number","null"]},"buttonBorderRadius":{"type":["number","null"]}},"additionalProperties":false},"AppFeatures":{"type":"object","properties":{"workoutTrackingEnabled":{"type":["boolean","null"]},"customWorkoutsEnabled":{"type":["boolean","null"]},"nutritionTrackingEnabled":{"type":["boolean","null"]},"chatEnabled":{"type":["boolean","null"]},"schedulingEnabled":{"type":["boolean","null"]},"progressTrackingEnabled":{"type":["boolean","null"]},"checkInFormId":{"type":["string","null"]},"initialOnboardingFormId":{"type":["string","null"]},"progressTrackingConfig":{"anyOf":[{"$ref":"#/components/schemas/ProgressTrackingConfig"},{"type":"null"}]},"dailyHabitTrackingEnabled":{"type":["boolean","null"]},"dailyHabitFormId":{"type":["string","null"]},"dailyHabitConfig":{"anyOf":[{"$ref":"#/components/schemas/DailyHabitTrackingConfig"},{"type":"null"}]},"wearableIntegrationEnabled":{"type":["boolean","null"]},"shopEnabled":{"type":["boolean","null"]},"shopSettings":{"anyOf":[{"$ref":"#/components/schemas/ShopSettings"},{"type":"null"}]},"programCardsConfig":{"anyOf":[{"$ref":"#/components/schemas/ProgramCardsConfig"},{"type":"null"}]},"multiLanguageEnabled":{"type":["boolean","null"]},"supportedLanguages":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"allowCalendarAccess":{"type":["boolean","null"]},"allowCustomProgressTracking":{"type":["boolean","null"]},"showPastPrograms":{"type":["boolean","null"]}},"additionalProperties":false},"ProgressTrackingConfig":{"type":"object","properties":{"metrics":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ProgressTrackingItem"}},{"type":"null"}]}},"additionalProperties":false},"ProgressTrackingItem":{"type":"object","properties":{"metricType":{"$ref":"#/components/schemas/ProgressMetricType"},"order":{"type":"number"},"displayName":{"type":["string","null"]},"pinned":{"type":"boolean"}},"required":["metricType","order","pinned"],"additionalProperties":false},"ProgressMetricType":{"type":"string","enum":["WEIGHT","BODY_FAT_PERCENTAGE","MUSCLE_MASS_PERCENTAGE","CHEST","WAIST","HIPS","LEFT_ARM","RIGHT_ARM","LEFT_THIGH","RIGHT_THIGH","BENCH_PRESS_1RM","BENCH_PRESS_10RM","SQUAT_1RM","SQUAT_10RM","DEADLIFT_1RM","DEADLIFT_10RM","PULLUPS_MAX","KM_TIME","VO2_MAX","BLOOD_PRESSURE_SYSTOLIC","BLOOD_PRESSURE_DIASTOLIC","SLEEP_AVERAGE","SLEEP_QUALITY","ENERGY","MOOD","ADHERENCE","PHOTOS","NOTES"]},"DailyHabitTrackingConfig":{"type":"object","properties":{"habits":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/HabitTrackingItem"}},{"type":"null"}]}},"additionalProperties":false},"HabitTrackingItem":{"type":"object","properties":{"habitType":{"$ref":"#/components/schemas/HabitType"},"order":{"type":"number"},"displayName":{"type":["string","null"]},"pinned":{"type":"boolean"}},"required":["habitType","order","pinned"],"additionalProperties":false},"HabitType":{"type":"string","enum":["WATER_INTAKE","PROTEIN_INTAKE","VEGETABLE_SERVINGS","MEAL_PREP","NO_SUGAR","CALORIE_TRACKING","STEPS","STRETCHING","ACTIVE_MINUTES","SLEEP_HOURS","SLEEP_BEFORE_MIDNIGHT","MEDITATION","FOAM_ROLLING","ICE_BATH","SAUNA","SUPPLEMENTS_TAKEN","JOURNALING","GRATITUDE","NO_ALCOHOL","NO_SMOKING","MINDFUL_EATING"]},"ShopSettings":{"type":"object","properties":{"iosSharedSecret":{"type":["string","null"]},"androidServiceAccountJson":{"type":["string","null"]},"androidPackageName":{"type":["string","null"]},"mobileIapEnabled":{"type":["boolean","null"]}},"additionalProperties":false},"ProgramCardsConfig":{"type":"object","properties":{"workoutProgramName":{"type":["string","null"]},"contentOnlyProgramName":{"type":["string","null"]},"nutritionProgramName":{"type":["string","null"]},"workoutProgramImage":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"contentOnlyProgramImage":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"nutritionProgramImage":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]}},"additionalProperties":false},"AppNavigation":{"type":"object","properties":{"tabBarItems":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/TabBarItem"}},{"type":"null"}]},"homeScreen":{"type":["string","null"]},"showBottomTabBar":{"type":["boolean","null"]},"showDrawerMenu":{"type":["boolean","null"]}},"additionalProperties":false},"TabBarItem":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"icon":{"type":["string","null"]},"screen":{"type":"string"},"enabled":{"type":"boolean"},"order":{"type":"number"}},"required":["id","label","screen","enabled","order"],"additionalProperties":false},"AppNotifications":{"type":"object","properties":{"pushNotificationsEnabled":{"type":["boolean","null"]},"workoutRemindersEnabled":{"type":["boolean","null"]},"appointmentRemindersEnabled":{"type":["boolean","null"]},"chatNotificationsEnabled":{"type":["boolean","null"]},"progressRemindersEnabled":{"type":["boolean","null"]},"marketingNotificationsEnabled":{"type":["boolean","null"]},"templates":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/NotificationTemplate"}},{"type":"null"}]}},"additionalProperties":false},"NotificationTemplate":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"title":{"type":["string","null"]},"body":{"type":["string","null"]},"enabled":{"type":["boolean","null"]}},"required":["id","type"],"additionalProperties":false},"AppContent":{"type":"object","properties":{"welcomeTitle":{"type":["string","null"]},"welcomeMessage":{"type":["string","null"]},"termsOfServiceUrl":{"type":["string","null"]},"termsOfServiceText":{"type":["string","null"]},"privacyPolicyUrl":{"type":["string","null"]},"privacyPolicyText":{"type":["string","null"]},"aboutText":{"type":["string","null"]}},"additionalProperties":false},"AppStoreSettings":{"type":"object","properties":{"iosAppName":{"type":["string","null"]},"iosSubtitle":{"type":["string","null"]},"iosDescription":{"type":["string","null"]},"iosKeywords":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"iosCategory":{"type":["string","null"]},"iosBundleId":{"type":["string","null"]},"androidAppName":{"type":["string","null"]},"androidShortDescription":{"type":["string","null"]},"androidFullDescription":{"type":["string","null"]},"androidCategory":{"type":["string","null"]},"androidPackageName":{"type":["string","null"]},"webAppUrl":{"type":["string","null"]},"screenshotMediaIds":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"supportEmail":{"type":["string","null"]},"privacyPolicyUrl":{"type":["string","null"]},"marketingUrl":{"type":["string","null"]}},"additionalProperties":false},"StoreConfiguration":{"type":"object","properties":{"enabled":{"type":"boolean"},"headerImageMedia":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"items":{"type":"array","items":{"$ref":"#/components/schemas/StoreItem"}}},"required":["enabled","items"],"additionalProperties":false},"StoreItem":{"type":"object","properties":{"id":{"type":"string"},"productId":{"type":"string"},"order":{"type":"number"},"genderFilter":{"$ref":"#/components/schemas/StoreGenderFilter"},"featured":{"type":"boolean"},"width":{"$ref":"#/components/schemas/StoreItemWidth"},"height":{"$ref":"#/components/schemas/StoreItemHeight"},"hideWhenActiveProductIds":{"type":"array","items":{"type":"string"}},"showOnMainScreen":{"type":"boolean"},"product":{"anyOf":[{"$ref":"#/components/schemas/ProductDto"},{"type":"null"}]}},"required":["id","productId","order","genderFilter","featured","width","height","hideWhenActiveProductIds","showOnMainScreen"],"additionalProperties":false},"StoreGenderFilter":{"type":"string","enum":["ALL","MALE","FEMALE"]},"StoreItemWidth":{"type":"string","enum":["HALF","FULL"]},"StoreItemHeight":{"type":"string","enum":["SMALL","NORMAL","LARGE"]},"ProductDto":{"$ref":"#/components/schemas/Record<string,unknown>"},"AppointmentAnalyticsDto":{"type":"object","properties":{"totalAppointments":{"type":"number"},"completedAppointments":{"type":"number"},"cancelledAppointments":{"type":"number"},"noShowAppointments":{"type":"number"},"attendanceRate":{"type":"number"},"averageSessionDuration":{"type":"number"},"mostPopularDays":{"$ref":"#/components/schemas/Record<string,number>"},"mostPopularTimes":{"$ref":"#/components/schemas/Record<string,number>"},"appointmentTypeBreakdown":{"$ref":"#/components/schemas/Record<string,number>"}},"required":["totalAppointments","completedAppointments","cancelledAppointments","noShowAppointments","attendanceRate","averageSessionDuration","mostPopularDays","mostPopularTimes","appointmentTypeBreakdown"],"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.scheduling.AppointmentAnalyticsDto."},"Record<string,number>":{"type":"object","additionalProperties":{"type":"number"}},"AppointmentDto":{"type":"object","properties":{"id":{"type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"isRecurring":{"type":["boolean","null"]},"recurrenceRule":{"type":["string","null"]},"isException":{"type":["boolean","null"]},"isTimeSensitive":{"type":["boolean","null"]},"startTime":{"anyOf":[{"type":"string"},{"type":"string","format":"date-time"},{"type":"null"}]},"endTime":{"anyOf":[{"type":"string"},{"type":"string","format":"date-time"},{"type":"null"}]},"originalStartTime":{"anyOf":[{"type":"string"},{"type":"string","format":"date-time"},{"type":"null"}]},"originalEndTime":{"anyOf":[{"type":"string"},{"type":"string","format":"date-time"},{"type":"null"}]},"title":{"type":["string","null"]},"description":{"type":["string","null"]},"type":{"anyOf":[{"$ref":"#/components/schemas/AppointmentType"},{"type":"null"}]},"status":{"anyOf":[{"$ref":"#/components/schemas/AppointmentStatus"},{"type":"null"}]},"modality":{"anyOf":[{"$ref":"#/components/schemas/AppointmentModality"},{"type":"null"}]},"location":{"type":["string","null"]},"virtualMeetingUrl":{"type":["string","null"]},"virtualMeetingId":{"type":["string","null"]},"virtualMeetingChannel":{"type":["string","null"]},"trainer":{"anyOf":[{"$ref":"#/components/schemas/UserDto"},{"type":"null"}]},"participantInfos":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ParticipantInfoDto"}},{"type":"null"}]},"user":{"anyOf":[{"$ref":"#/components/schemas/UserDto"},{"type":"null"}]},"workoutId":{"type":["string","null"]},"reminderSent":{"type":["boolean","null"]},"reminderHoursBefore":{"type":["number","null"]},"checkInRequired":{"type":["boolean","null"]},"cancellationReason":{"anyOf":[{"$ref":"#/components/schemas/CancellationReason"},{"type":"null"}]},"cancellationNote":{"type":["string","null"]},"noShowPrediction":{"type":["number","null"]},"engagementScore":{"type":["number","null"]},"checkInStatus":{"anyOf":[{},{"type":"null"}]},"metadata":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]},"updateStrategy":{"anyOf":[{"$ref":"#/components/schemas/AppointmentUpdateStrategy"},{"type":"null"}]},"seriesId":{"type":["string","null"]},"exceptionType":{"anyOf":[{"$ref":"#/components/schemas/ExceptionType"},{"type":"null"}]},"meetingTranscriptId":{"type":["string","null"]},"prepNotes":{"type":["string","null"]},"clientTimezoneId":{"type":["string","null"]},"purchaseId":{"type":["string","null"]},"programId":{"type":["string","null"]},"tenantId":{"type":["string","null"]}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.scheduling.AppointmentDto (@JsonInclude(NON_NULL)). Field order matches the Kotlin class. Used both as the inbound request body and as the outbound response shape. Nulls are stripped from responses (see toAppointmentDto).\n\nOn INPUT all fields are optional. `startTime`/`endTime`/`originalStartTime`/ `originalEndTime` are ISO-8601 instant strings on the wire; we keep them typed as string | Date so request JSON (string) and entity reads (Date) both flow through."},"AppointmentType":{"type":"string","enum":["PERSONAL_TRAINING","GROUP_SESSION","CONSULTATION","CUSTOM","CHECK_IN","LEAD","CLIENT_REMINDER"]},"AppointmentStatus":{"type":"string","enum":["SCHEDULED","CONFIRMED","COMPLETED","CANCELLED","RESCHEDULED","NO_SHOW","IN_PROGRESS"]},"AppointmentModality":{"type":"string","enum":["IN_PERSON","VIRTUAL","HYBRID","ASYNCHRONOUS"]},"UserDto":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"role":{"$ref":"#/components/schemas/UserRole"},"phoneNumber":{"type":"string"},"instagram":{"type":"string"},"facebook":{"type":"string"},"tiktok":{"type":"string"},"profilePicture":{"$ref":"#/components/schemas/Record<string,unknown>"},"isActive":{"type":"boolean"},"emailVerified":{"type":"boolean"},"dataProfilesCreated":{"type":"boolean"},"tenantId":{"type":"string"},"address":{"$ref":"#/components/schemas/Record<string,unknown>"},"trainerOnboardingStatus":{"type":"string"},"trainerOnboardingCompletedAt":{"type":"string","format":"date-time"},"trainerOnboardingSkippedAt":{"type":"string","format":"date-time"},"yearsExperience":{"type":"string"},"certifications":{"type":"array","items":{"type":"string"}},"trainingSpecializations":{"type":"array","items":{"type":"string"}},"trainingStyle":{"type":"string"},"targetDemographics":{"type":"array","items":{"type":"string"}},"referralSource":{"type":"string"},"unitSystem":{"type":"string"},"defaultCurrency":{"type":"string"},"language":{"type":"string"},"activitySettings":{"$ref":"#/components/schemas/Record<string,unknown>"},"teamPermissions":{"type":"array","items":{"$ref":"#/components/schemas/TeamPermission"}},"referralCode":{"type":"string"},"externalId":{"type":"string"},"lifecycleStageId":{"type":"string"},"lifecycleStageName":{"type":"string"},"lifecycleStageColor":{"type":"string"}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.user.UserDto (@JsonInclude(NON_NULL)). Null/undefined fields are omitted from the response — see toUserDto."},"UserRole":{"type":"string","enum":["TRAINER","CLIENT","ADMIN","SYSTEM"]},"TeamPermission":{"type":"string","enum":["OWNER","ADMIN","COACH"]},"ParticipantInfoDto":{"type":"object","properties":{"email":{"type":["string","null"]},"firstName":{"type":["string","null"]},"lastName":{"type":["string","null"]},"profilePicture":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"userId":{"type":["string","null"]},"leadId":{"type":["string","null"]}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.model.scheduling.ParticipantInfoDto. Stored as a jsonb array on meeting_transcripts.participant_infos. Passed through verbatim on the way in and out (no transformation), matching the Kotlin model."},"MediaType":{"type":"string","enum":["IMAGE","VIDEO","AUDIO","FILE","DOCUMENT","EXERCISE","WORKOUT","NUTRITION","POST"],"description":"Media-domain enums, mirroring com.protocolcrm.api.model.media.* exactly. All are stored as STRING in Postgres (Hibernate"},"MediaUsageType":{"type":"string","enum":["GENERAL","PROFILE","LIBRARY","CONVERSATION","DOCS"]},"DocReadableFormat":{"type":"string","enum":["HTML","MARKDOWN","TEXT"]},"ResourceVisibility":{"type":"string","enum":["PRIVATE","TEAM"],"description":"Library-resource visibility (programs/workouts/exercises/nutrition templates/media). Explicit replacement for the legacy \"owner_id IS NULL = team-shared\" convention: PRIVATE = owner + tenant owner/admins; TEAM = whole tenant (coaches can import a private copy). Only enforced when the tenant is in TEAM mode."},"MediaCategoryDto":{"type":"object","properties":{"id":{"type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"name":{"type":["string","null"]},"color":{"type":["string","null"]},"iconEmoji":{"type":["string","null"]},"order":{"type":["number","null"]},"tenantId":{"type":["string","null"]},"parentId":{"type":["string","null"]},"userId":{"type":["string","null"]},"resourceCount":{"type":["number","null"]},"categoryType":{"anyOf":[{"$ref":"#/components/schemas/CategoryType"},{"type":"null"}]},"description":{"type":["string","null"]},"avatarMedia":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"externalId":{"type":["string","null"]},"externalUpdatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["id","createdAt","updatedAt","name","color","iconEmoji","order","tenantId","parentId","userId","resourceCount","categoryType","description","avatarMedia","externalId","externalUpdatedAt"],"additionalProperties":false,"description":"MediaCategoryDto (default Jackson inclusion — ALL keys emitted, nulls kept). Optionality on the TS interface reflects the Kotlin nullable types, but every key is present in the serialized object (toMediaCategoryDto sets them all)."},"CategoryType":{"type":"string","enum":["MEDIA","NUTRITION","WORKOUT","PROGRAM","EXERCISE","PROGRAM_WORKOUT","PROGRAM_NUTRITION"]},"Record<string,string>":{"type":"object","additionalProperties":{"type":"string"}},"CancellationReason":{"type":"string","enum":["CLIENT_REQUEST","TRAINER_REQUEST","EMERGENCY","ILLNESS","SCHEDULING_CONFLICT","TECHNICAL_ISSUES","WEATHER","NO_REASON_PROVIDED","OTHER"]},"AppointmentUpdateStrategy":{"type":"string","enum":["ALL","CURRENT","CURRENT_AND_NEXT","DELETE_EXCEPTION"]},"ExceptionType":{"type":"string","enum":["MODIFICATION","CANCELLATION","RESCHEDULED"]},"AppointmentFiltersResponseDto":{"type":"object","properties":{"appointmentTypes":{"type":"array","items":{"type":"string"}},"statusOptions":{"type":"array","items":{"type":"string"}},"modalityOptions":{"type":"array","items":{"type":"string"}},"cancellationReasons":{"type":"array","items":{"type":"string"}}},"required":["appointmentTypes","statusOptions","modalityOptions","cancellationReasons"],"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.scheduling.AppointmentFiltersResponseDto."},"AppointmentListFilters":{"type":"object","properties":{"page":{"type":"number"},"pageSize":{"type":"number"},"startDate":{"type":"string","format":"date-time"},"endDate":{"type":"string","format":"date-time"},"type":{"$ref":"#/components/schemas/AppointmentType"},"status":{"type":"array","items":{"$ref":"#/components/schemas/AppointmentStatus"}},"modality":{"$ref":"#/components/schemas/AppointmentModality"},"participantId":{"type":"string"},"trainerId":{"type":"array","items":{"type":"string"}},"userId":{"type":"string"},"excludedType":{"type":"array","items":{"$ref":"#/components/schemas/AppointmentType"}},"timezoneId":{"type":"string"}},"required":["page","pageSize","timezoneId"],"additionalProperties":false,"description":"Filter params for the list endpoint. 1-indexed page (Kotlin default page=1)."},"AutoOnboardingQueueItemDto":{"type":"object","properties":{"taskId":{"type":["string","null"]},"userId":{"type":["string","null"]},"userName":{"type":["string","null"]},"userEmail":{"type":["string","null"]},"status":{"type":["string","null"]},"steps":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ScheduledTaskStep"}},{"type":"null"}]},"lastLog":{"type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.user.AutoOnboardingQueueItemDto (@JsonInclude(NON_NULL)). Null/undefined fields are stripped from the response."},"ScheduledTaskStep":{"type":"object","properties":{"name":{"type":["string","null"]},"description":{"type":["string","null"]},"status":{"type":["string","null"]}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.model.ScheduledTaskStep."},"AutomationDto":{"type":"object","properties":{"id":{"type":["string","null"]},"tenantId":{"type":["string","null"]},"name":{"type":"string"},"kind":{"$ref":"#/components/schemas/AutomationKind"},"status":{"$ref":"#/components/schemas/AutomationStatus"},"config":{"$ref":"#/components/schemas/Record<string,unknown>"},"triggerConfig":{"$ref":"#/components/schemas/Record<string,unknown>"},"triggerCount":{"type":"number"},"lastTriggeredAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["id","tenantId","name","kind","status","config","triggerConfig","triggerCount","lastTriggeredAt","createdAt","updatedAt"],"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.automation.AutomationDto (NON_NULL)."},"AutomationKind":{"type":"string","const":"PROGRESS_REPORT"},"AutomationStatus":{"type":"string","enum":["DRAFT","ACTIVE","PAUSED","ARCHIVED"]},"AutomationRunDto":{"type":"object","properties":{"id":{"type":["string","null"]},"tenantId":{"type":["string","null"]},"automationId":{"type":["string","null"]},"status":{"$ref":"#/components/schemas/RunStatus"},"triggerSource":{"$ref":"#/components/schemas/RunTriggerSource"},"triggerData":{"$ref":"#/components/schemas/Record<string,unknown>"},"gatheredData":{"$ref":"#/components/schemas/Record<string,unknown>"},"prompt":{"type":["string","null"]},"rawResponse":{"type":["string","null"]},"parsedOutput":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]},"outputEntityType":{"type":["string","null"]},"outputEntityId":{"type":["string","null"]},"inputTokens":{"type":"number"},"outputTokens":{"type":"number"},"errorMessage":{"type":["string","null"]},"startedAt":{"type":"string","format":"date-time"},"completedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["id","tenantId","automationId","status","triggerSource","triggerData","gatheredData","prompt","rawResponse","parsedOutput","outputEntityType","outputEntityId","inputTokens","outputTokens","errorMessage","startedAt","completedAt"],"additionalProperties":false},"RunStatus":{"type":"string","enum":["RUNNING","COMPLETED","FAILED"]},"RunTriggerSource":{"type":"string","enum":["EVENT","MANUAL"]},"Background":{"type":"object","properties":{"type":{"type":"string"},"value":{"type":"string"}},"additionalProperties":false},"BillingInterval":{"type":"string","enum":["MONTH","YEAR"]},"BillingPortalRequest":{"type":"object","properties":{"returnUrl":{"type":"string"}},"required":["returnUrl"],"additionalProperties":false,"description":"Request DTO for creating a billing portal session."},"BillingPortalResponse":{"type":"object","properties":{"portalUrl":{"type":"string"}},"required":["portalUrl"],"additionalProperties":false,"description":"Response DTO for billing portal session creation."},"BiometricDataPointDto":{"type":"object","properties":{"value":{"type":"number"},"measureDate":{"type":["string","null"]},"measureTime":{"type":["string","null"]},"sourceDevice":{"type":["string","null"]}},"required":["value"],"additionalProperties":false},"BiometricDataPointUpdateDto":{"type":"object","properties":{"metricType":{"$ref":"#/components/schemas/BiometricType"},"value":{"type":"number"},"measureDate":{"type":"string"},"measureTime":{"type":"string"},"sourceDevice":{"type":["string","null"]}},"required":["metricType","value","measureDate","measureTime"],"additionalProperties":false},"BiometricType":{"type":"string","enum":["WEIGHT","BODY_FAT","MUSCLE_MASS","BMI","RESTING_HEART_RATE","BP_SYSTOLIC","BP_DIASTOLIC","HRV","VO2MAX","SLEEP_DURATION","SLEEP_QUALITY","DEEP_SLEEP","REM_SLEEP","LIGHT_SLEEP","SLEEP_LATENCY","SLEEP_CONSISTENCY","SLEEP_BREATHING","SLEEP_EFFICIENCY","RECOVERY_SCORE","READINESS_SCORE","STRAIN_SCORE"]},"BiometricDataPointsUpdateRequestDto":{"type":"object","properties":{"dataPoints":{"type":"array","items":{"$ref":"#/components/schemas/BiometricDataPointUpdateDto"}}},"required":["dataPoints"],"additionalProperties":false},"BiometricProfileResponseDto":{"type":"object","properties":{"weight":{"$ref":"#/components/schemas/MetricDataDto"},"bodyFatPercentage":{"$ref":"#/components/schemas/MetricDataDto"},"muscleMass":{"$ref":"#/components/schemas/MetricDataDto"},"bmi":{"$ref":"#/components/schemas/MetricDataDto"},"restingHeartRate":{"$ref":"#/components/schemas/MetricDataDto"},"bloodPressureSystolic":{"$ref":"#/components/schemas/MetricDataDto"},"bloodPressureDiastolic":{"$ref":"#/components/schemas/MetricDataDto"},"heartRateVariability":{"$ref":"#/components/schemas/MetricDataDto"},"vo2Max":{"$ref":"#/components/schemas/MetricDataDto"},"sleepDuration":{"$ref":"#/components/schemas/MetricDataDto"},"sleepQualityScore":{"$ref":"#/components/schemas/MetricDataDto"},"deepSleepPercentage":{"$ref":"#/components/schemas/MetricDataDto"},"remSleepPercentage":{"$ref":"#/components/schemas/MetricDataDto"},"lightSleepPercentage":{"$ref":"#/components/schemas/MetricDataDto"},"sleepLatency":{"$ref":"#/components/schemas/MetricDataDto"},"sleepConsistencyScore":{"$ref":"#/components/schemas/MetricDataDto"},"sleepBreathingDisturbances":{"$ref":"#/components/schemas/MetricDataDto"},"sleepEfficiency":{"$ref":"#/components/schemas/MetricDataDto"},"recoveryScore":{"$ref":"#/components/schemas/MetricDataDto"},"readinessScore":{"$ref":"#/components/schemas/MetricDataDto"},"strainScore":{"$ref":"#/components/schemas/MetricDataDto"},"dataSourcesConnected":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"lastSyncDate":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"lastUpdatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"additionalProperties":false,"description":"Mutable accumulator; we strip nullish per-field at serialization time."},"MetricDataDto":{"type":"object","properties":{"latest":{"type":["number","null"]},"average":{"type":["number","null"]},"min":{"type":["number","null"]},"max":{"type":["number","null"]},"optimalMin":{"type":["number","null"]},"optimalMax":{"type":["number","null"]},"unit":{"type":["string","null"]},"dataPoints":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/BiometricDataPointDto"}},{"type":"null"}]}},"additionalProperties":false},"BookingAvailability":{"type":"object","properties":{"dayOfWeek":{"type":"number"},"startTime":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"number"}}]},"endTime":{"anyOf":[{"type":"string"},{"type":"array","items":{"type":"number"}}]},"isActive":{"type":"boolean"},"timezoneId":{"type":"string"}},"required":["dayOfWeek","startTime","endTime","isActive","timezoneId"],"additionalProperties":false},"BookingBranding":{"type":"object","properties":{"logoMedia":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"faviconMedia":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"companyName":{"type":["string","null"]},"tagline":{"type":["string","null"]},"showTrainerPhoto":{"type":"boolean"},"customCss":{"type":["string","null"]},"customHeaderHtml":{"type":["string","null"]},"customFooterHtml":{"type":["string","null"]}},"additionalProperties":false},"BookingEmailSettings":{"type":"object","properties":{"confirmationEmailEnabled":{"type":"boolean"},"reminderEmailEnabled":{"type":"boolean"},"reminderHoursBefore":{"type":"number"},"fromName":{"type":["string","null"]},"replyToEmail":{"type":["string","null"]},"ccEmails":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"includeIcsAttachment":{"type":"boolean"},"confirmationSubject":{"type":["string","null"]},"reminderSubject":{"type":["string","null"]},"cancellationSubject":{"type":["string","null"]},"confirmationEmailTemplate":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"reminderEmailTemplate":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"cancellationEmailTemplate":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]}},"additionalProperties":false},"BookingPalette":{"type":"object","properties":{"mode":{"type":"string"},"primary":{"type":"string"},"secondary":{"type":"string"},"background":{"type":"string"},"surface":{"type":"string"},"textPrimary":{"type":"string"},"textSecondary":{"type":"string"},"buttonBackground":{"type":"string"},"buttonText":{"type":"string"},"error":{"type":"string"},"success":{"type":"string"}},"additionalProperties":false},"BookingSettings":{"type":"object","properties":{"minimumAdvanceHours":{"type":"number"},"maximumAdvanceDays":{"type":"number"},"bufferMinutesBefore":{"type":"number"},"bufferMinutesAfter":{"type":"number"},"requireClientDetails":{"type":"boolean"},"autoConfirmBookings":{"type":"boolean"},"isPublicBookingEnabled":{"type":"boolean"},"defaultRedirectUrl":{"type":["string","null"]},"theme":{"anyOf":[{"$ref":"#/components/schemas/BookingTheme"},{"type":"null"}]},"branding":{"anyOf":[{"$ref":"#/components/schemas/BookingBranding"},{"type":"null"}]},"emailSettings":{"anyOf":[{"$ref":"#/components/schemas/BookingEmailSettings"},{"type":"null"}]}},"required":["minimumAdvanceHours","maximumAdvanceDays","bufferMinutesBefore","bufferMinutesAfter","requireClientDetails","autoConfirmBookings","isPublicBookingEnabled"],"additionalProperties":false},"BookingTheme":{"type":"object","properties":{"preset":{"type":"string"},"palette":{"$ref":"#/components/schemas/BookingPalette"},"typography":{"$ref":"#/components/schemas/BookingTypography"},"shape":{"$ref":"#/components/schemas/BookingShape"}},"additionalProperties":false},"BookingTypography":{"type":"object","properties":{"fontFamily":{"type":"string"},"heading":{"$ref":"#/components/schemas/TypographyStyle"},"body":{"$ref":"#/components/schemas/TypographyStyle"},"button":{"$ref":"#/components/schemas/TypographyStyle"},"caption":{"$ref":"#/components/schemas/TypographyStyle"}},"additionalProperties":false},"TypographyStyle":{"type":"object","properties":{"fontSize":{"type":"string"},"fontWeight":{"type":"string"},"lineHeight":{"type":"string"},"letterSpacing":{"type":"string"}},"additionalProperties":false,"description":"Ports the marketing booking DTOs (com.protocolcrm.api.dto.marketing.*):   BookingDTO.kt, BookingSettings.kt, BookingSettingsOverride.kt, BookingTheme.kt,   BookingBranding.kt, BookingEmailSettings.kt, BookingEventConfiguration.kt.\n\nALL of these data classes are"},"BookingShape":{"type":"object","properties":{"borderRadius":{"type":"number"},"cardElevation":{"type":"number"},"spacing":{"type":"number"},"buttonBorderRadius":{"type":"number"}},"additionalProperties":false},"BookingSettingsOverride":{"type":"object","properties":{"minimumAdvanceHours":{"type":["number","null"]},"maximumAdvanceDays":{"type":["number","null"]},"bufferMinutesBefore":{"type":["number","null"]},"bufferMinutesAfter":{"type":["number","null"]},"requireClientDetails":{"type":["boolean","null"]},"autoConfirmBookings":{"type":["boolean","null"]},"isPublicBookingEnabled":{"type":["boolean","null"]},"redirectUrl":{"type":["string","null"]},"theme":{"anyOf":[{"$ref":"#/components/schemas/BookingTheme"},{"type":"null"}]},"emailSettings":{"anyOf":[{"$ref":"#/components/schemas/BookingEmailSettings"},{"type":"null"}]}},"additionalProperties":false},"CheckInMapTo":{"type":"string","enum":["WEIGHT","BODY_FAT","BODY_FAT_WEIGHT","MUSCLE_MASS_PERCENTAGE","MUSCLE_MASS_WEIGHT","LEAN_MASS_PERCENTAGE","LEAN_MASS_WEIGHT","HEIGHT","WAIST","WAIST_AT_NAVEL","HIPS","CHEST","LEFT_ARM","RIGHT_ARM","LEFT_THIGH","RIGHT_THIGH","LEFT_ABOVE_KNEE","RIGHT_ABOVE_KNEE","LEFT_CALF","RIGHT_CALF","BMI","BENCH_PRESS_1RM","BENCH_PRESS_10RM","SQUAT_1RM","SQUAT_10RM","DEADLIFT_1RM","DEADLIFT_10RM","PULLUPS_MAX","ONE_KM_TIME","VO2_MAX","BP_SYS","BP_DIA","SLEEP_AVG_H","SLEEP_QUALITY","ENERGY","MOOD","ADHERENCE","CLIENT_NOTES","TRAINER_NOTES","IMAGES_ALL_SIDES","CUSTOM","WATER_INTAKE","PROTEIN_INTAKE","VEGETABLE_SERVINGS","MEAL_PREP","NO_SUGAR","CALORIE_TRACKING","STEPS","STRETCHING","ACTIVE_MINUTES","SLEEP_HOURS","SLEEP_BEFORE_MIDNIGHT","MEDITATION","FOAM_ROLLING","ICE_BATH","SAUNA","SUPPLEMENTS_TAKEN","JOURNALING","GRATITUDE","NO_ALCOHOL","NO_SMOKING","MINDFUL_EATING","INFO"],"description":"CheckInMapTo enum. Modelled as a string union (the full set of names from the Kotlin enum). Values are passed through verbatim inside Question/FormAnswer JSON."},"ClientAuthCodeResponseDto":{"type":"object","properties":{"code":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"},"clientId":{"type":"string"}},"required":["code","expiresAt","clientId"],"additionalProperties":false,"description":"Mirrors ClientAuthCodeResponseDto."},"ClientInsightDto":{"type":"object","properties":{"id":{"type":["string","null"]},"userId":{"type":["string","null"]},"user":{"anyOf":[{"$ref":"#/components/schemas/UserDto"},{"type":"null"}]},"type":{"anyOf":[{"$ref":"#/components/schemas/InsightType"},{"type":"null"}]},"severity":{"anyOf":[{"$ref":"#/components/schemas/InsightSeverity"},{"type":"null"}]},"title":{"type":["string","null"]},"description":{"type":["string","null"]},"recommendation":{"type":["string","null"]},"priority":{"type":"number"},"isRead":{"type":"boolean"},"isDismissed":{"type":"boolean"},"metadata":{"type":["string","null"]},"insightDate":{"type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["id","userId","user","type","severity","title","description","recommendation","priority","isRead","isDismissed","metadata","insightDate","createdAt","updatedAt"],"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.insights.ClientInsightDto.\n\nNOTE: this DTO has NO"},"InsightType":{"type":"string","enum":["RETENTION_RISK","SUCCESS_PATTERN","NUTRITION_OPTIMIZATION","REVENUE_OPPORTUNITY","PERFORMANCE_TREND","WORKOUT_OPTIMIZATION","SCHEDULE_OPTIMIZATION","GOAL_ACHIEVEMENT","PLATEAU_WARNING","ENGAGEMENT_DROP"],"description":"Mirrors com.protocolcrm.api.model.insights.ClientInsight (table: client_insights, extends BaseTenantEntity). Flyway owns DDL; column names match the Kotlin"},"InsightSeverity":{"type":"string","enum":["INFO","SUCCESS","WARNING","ERROR"]},"ClientProgressReportDto":{"type":"object","properties":{"id":{"type":"string"},"progressEntryId":{"type":"string"},"status":{"$ref":"#/components/schemas/ReportStatus"},"priority":{"$ref":"#/components/schemas/ReportPriority"},"clientFacingSummary":{"type":"string"},"sections":{"$ref":"#/components/schemas/Record<string,unknown>"},"approvedAt":{"type":"string","format":"date-time"},"openedAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["status","priority","sections"],"additionalProperties":false,"description":"Client-facing projection of a delivered report. Returned to CLIENT callers from list/getById/markOpened. Deliberately OMITS every internal/coach-only field (internalNotes, gatheredDataSnapshot, approvedByUserId, automationId/RunId, clientNotificationId, dispatchedToClient, tenantId) — a client must never see them."},"ReportStatus":{"type":"string","enum":["DRAFT","APPROVED","DISCARDED","FAILED"],"description":"Mirrors com.protocolcrm.api.model.progress.ReportStatus / ReportPriority. Stored as the enum NAME in a VARCHAR column (Kotlin"},"ReportPriority":{"type":"string","enum":["STANDARD","URGENT"]},"ClientsByStageEntry":{"type":"object","properties":{"stageId":{"type":"string"},"stageName":{"type":["string","null"]},"stageColor":{"type":["string","null"]},"count":{"type":"number"}},"required":["stageId","stageName","stageColor","count"],"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.DashboardOverviewDto and its nested entries. DashboardOverviewDto HAS"},"ColumnReorderDto":{"type":"object","properties":{"columnIds":{"type":"array","items":{"type":"string"}}},"required":["columnIds"],"additionalProperties":false},"ConfigDto":{"type":"object","properties":{"id":{"type":["string","null"]},"key":{"type":["string","null"]},"data":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]},"tags":{"type":"array","items":{"type":"string"}},"userId":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","key","data","tags","userId","createdAt","updatedAt"],"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.util.ConfigDto. No"},"ConsumeSessionRequest":{"type":"object","properties":{"purchaseId":{"type":["string","null"]},"sessionsToConsume":{"type":["number","null"]}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.shop.ConsumeSessionRequest (sessionsToConsume defaults 1)."},"ConsumeSessionResponse":{"type":"object","properties":{"purchaseId":{"type":"string"},"sessionsConsumed":{"type":"number"},"sessionsRemaining":{"type":"number"},"totalSessions":{"type":"number"}},"required":["purchaseId","sessionsConsumed","sessionsRemaining","totalSessions"],"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.shop.ConsumeSessionResponse. NOT"},"ConversationDraftSummary":{"type":"object","properties":{"preview":{"type":"string"},"hasMedia":{"type":"boolean"},"updatedById":{"type":["string","null"]},"updatedByName":{"type":["string","null"]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["preview","hasMedia","updatedById","updatedByName","updatedAt"],"additionalProperties":false,"description":"Lightweight per-conversation draft summary — rides the conversations-list response and the DRAFT_UPDATED realtime event so the sidebar can render \"Draft: …\" without the full body. Never carries the full text (the composer fetches that via GET on open)."},"ConversationDto":{"type":"object","properties":{"id":{"type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"title":{"type":["string","null"]},"type":{"anyOf":[{"$ref":"#/components/schemas/ConversationType"},{"type":"null"}]},"isActive":{"type":["boolean","null"]},"lastActivityAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"creatorId":{"type":["string","null"]},"image":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"participants":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ParticipantDto"}},{"type":"null"}]},"lastMessage":{"anyOf":[{"$ref":"#/components/schemas/LastMessageDto"},{"type":"null"}]},"metadata":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]},"draft":{"anyOf":[{"$ref":"#/components/schemas/ConversationDraftSummary"},{"type":"null"}]}},"additionalProperties":false,"description":"ConversationDto — declaration order: id, createdAt, updatedAt (BaseTenantDto overrides) then title, type, isActive, lastActivityAt, creatorId, image, participants, lastMessage, metadata. NO"},"ConversationType":{"type":"string","enum":["DIRECT","GROUP","INFO"],"description":"Mirrors com.protocolcrm.api.model.communication.ConversationModel (table: conversations, extends BaseTenantEntity). Flyway owns DDL; synchronize:false.\n\n`type` is Hibernate"},"ParticipantDto":{"type":"object","properties":{"id":{"type":["string","null"]},"name":{"type":["string","null"]},"avatarUrl":{"type":["string","null"]},"role":{"type":["string","null"]},"canPost":{"type":["boolean","null"]}},"required":["id","name","avatarUrl","role","canPost"],"additionalProperties":false,"description":"ParticipantDto — declaration order: id, name, avatarUrl. NO"},"LastMessageDto":{"type":"object","properties":{"id":{"type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"content":{"type":["string","null"]},"type":{"anyOf":[{"$ref":"#/components/schemas/MessageType"},{"type":"null"}]},"source":{"anyOf":[{"$ref":"#/components/schemas/MessageSource"},{"type":"null"}]},"senderId":{"type":["string","null"]},"senderName":{"type":["string","null"]},"status":{"anyOf":[{"$ref":"#/components/schemas/MessageStatus"},{"type":"null"}]},"sentAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"isPinned":{"type":["boolean","null"]},"unreadCount":{"type":["number","null"]}},"required":["id","createdAt","updatedAt","content","type","source","senderId","senderName","status","sentAt","isPinned","unreadCount"],"additionalProperties":false,"description":"LastMessageDto — declaration order: id, createdAt, updatedAt, content, type, source, senderId, senderName, status, sentAt, isPinned, unreadCount. NO"},"MessageType":{"type":"string","enum":["CHAT","WORKOUT_SHARE","NUTRITION_SHARE","CHECK_IN_PROMPT","SCHEDULING","AI_INSIGHT","SYSTEM_NOTIFICATION"],"description":"Mirrors com.protocolcrm.api.model.communication.MessageModel (table: messages, extends BaseTenantEntity). Flyway owns DDL; synchronize:false.\n\nEnum columns `type` (MessageType) and `source` (MessageSource) are Hibernate"},"MessageSource":{"type":"string","enum":["USER","AI","SYSTEM","SCHEDULED","FORWARDED"]},"MessageStatus":{"type":"string","enum":["SENT","DELIVERED","READ","FAILED"]},"ConversationMediaItemDto":{"type":"object","properties":{"kind":{"$ref":"#/components/schemas/ConversationMediaKind"},"messageId":{"type":"string"},"senderId":{"type":["string","null"]},"sentAt":{"type":"string"},"media":{"$ref":"#/components/schemas/MediaDto","description":"Present for MEDIA and FILE."},"url":{"type":"string","description":"Present for LINK — the destination URL."},"linkText":{"type":["string","null"],"description":"Present for LINK — the anchor text (the slice of message content the link covers)."}},"required":["kind","messageId","senderId","sentAt"],"additionalProperties":false,"description":"One shared item in a conversation: a media attachment (MEDIA/FILE) or a link (LINK)."},"ConversationMediaKind":{"type":"string","enum":["MEDIA","FILE","LINK"]},"ConversationPresenceDto":{"type":"object","properties":{"conversationId":{"type":"string"},"users":{"type":"array","items":{"$ref":"#/components/schemas/UserPresenceDto"}},"totalParticipants":{"type":"number"}},"required":["conversationId","users","totalParticipants"],"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.conversation.ConversationPresenceDto (no"},"UserPresenceDto":{"type":"object","properties":{"userId":{"type":"string"},"isOnline":{"type":"boolean"},"lastSeen":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["userId","isOnline","lastSeen"],"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.conversation.UserPresenceDto (no"},"CreateAgentConnectionDto":{"type":"object","properties":{"label":{"type":["string","null"]},"tier":{"$ref":"#/components/schemas/AgentScopeTier","description":"Scope tier for the new key. One of 'read' | 'write' | 'send'. Defaults to 'write'."}},"additionalProperties":false,"description":"Accepted body for creating a connection. `label` and the optional scope `tier` are client-supplied."},"CreateAutomationRequest":{"type":"object","properties":{"name":{"type":"string"},"kind":{"$ref":"#/components/schemas/AutomationKind"},"config":{"$ref":"#/components/schemas/Record<string,unknown>"},"triggerConfig":{"$ref":"#/components/schemas/Record<string,unknown>"}},"required":["name","kind"],"additionalProperties":false},"CreateMediaShareRequestDto":{"type":"object","properties":{"mediaId":{"type":"string"},"shareType":{"$ref":"#/components/schemas/ShareType"},"permission":{"anyOf":[{"$ref":"#/components/schemas/SharePermission"},{"type":"null"}]},"sharedWithUserIds":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"expiresAt":{"type":["string","null"]},"sendNotification":{"type":["boolean","null"]}},"required":["mediaId","shareType"],"additionalProperties":false,"description":"Ports com.protocolcrm.api.dto.media.MediaShareDto.kt. Every DTO in that file is"},"ShareType":{"type":"string","enum":["PUBLIC","USER_SPECIFIC"]},"SharePermission":{"type":"string","enum":["READ","WRITE"]},"CreateMediaShareResponseDto":{"type":"object","properties":{"shares":{"type":"array","items":{"$ref":"#/components/schemas/MediaShareDto"}},"publicUrl":{"type":"string"},"notificationsSent":{"type":"number"}},"required":["shares","notificationsSent"],"additionalProperties":false},"MediaShareDto":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"mediaId":{"type":"string"},"ownerId":{"type":"string"},"ownerName":{"type":"string"},"shareType":{"$ref":"#/components/schemas/ShareType"},"permission":{"$ref":"#/components/schemas/SharePermission"},"sharedWithUserId":{"type":"string"},"sharedWithUserName":{"type":"string"},"sharedWithUserEmail":{"type":"string"},"publicToken":{"type":"string"},"publicUrl":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"},"isActive":{"type":"boolean"},"tenantId":{"type":"string"}},"additionalProperties":false},"CreateNutritionLogDto":{"type":"object","properties":{"userId":{"type":["string","null"]},"date":{"type":["string","null"]},"time":{"type":["string","null"]},"inputType":{"$ref":"#/components/schemas/NutritionLogInputType"},"originalInput":{"type":["string","null"]},"imageId":{"type":["string","null"]},"items":{"type":"array","items":{"$ref":"#/components/schemas/NutritionLogItem"}},"nutritionTemplateId":{"type":["string","null"]},"notes":{"type":["string","null"]}},"required":["inputType","items"],"additionalProperties":false},"NutritionLogInputType":{"type":"string","enum":["TEXT","IMAGE"]},"NutritionLogItem":{"type":"object","properties":{"id":{"type":["string","null"]},"name":{"type":["string","null"]},"englishName":{"type":["string","null"]},"quantity":{"type":["number","null"]},"measureLabel":{"type":["string","null"]},"measureUri":{"type":["string","null"]},"foodId":{"type":["string","null"]},"foodImage":{"type":["string","null"]},"nutrients":{"anyOf":[{"$ref":"#/components/schemas/NutrientsInfo"},{"type":"null"}]},"confidence":{"type":["number","null"]},"source":{"anyOf":[{"$ref":"#/components/schemas/NutritionItemSource"},{"type":"null"}]}},"additionalProperties":false},"NutrientsInfo":{"type":"object","properties":{"calories":{"type":["number","null"]},"carbs":{"type":["number","null"]},"protein":{"type":["number","null"]},"fat":{"type":["number","null"]},"fiber":{"type":["number","null"]}},"additionalProperties":false},"NutritionItemSource":{"type":"string","enum":["COACH_DB","PROTOCOL_DB","AI","MANUAL"],"description":"ECS-1104: provenance of a logged item's nutrients — coach's tenant food DB, the public Protocol library, a raw AI estimate (no DB match), or a manually entered food (client typed the macros, e.g. from a label; client-side only)."},"CreateSubscriptionCheckoutRequest":{"type":"object","properties":{"additionalSeats":{"type":"number"},"successUrl":{"type":"string"},"cancelUrl":{"type":"string"}},"required":["successUrl","cancelUrl"],"additionalProperties":false,"description":"Request DTO for creating a Protocol Pro subscription checkout session."},"CreateWorkoutSessionRequestDto":{"type":"object","properties":{"clientSessionId":{"type":"string"},"workoutId":{"type":["string","null"]},"isCustom":{"type":["boolean","null"]},"customWorkoutName":{"type":["string","null"]},"status":{"type":"string"},"exercisesCompleted":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/SessionExerciseDto"}},{"type":"null"}]},"energyLevelBefore":{"type":["number","null"]}},"required":["clientSessionId"],"additionalProperties":false},"SessionExerciseDto":{"type":"object","properties":{"exerciseId":{"type":"string"},"exerciseName":{"type":["string","null"]},"order":{"type":"number"},"units":{"type":"array","items":{"$ref":"#/components/schemas/SetDataType"}},"completedSets":{"type":"array","items":{"$ref":"#/components/schemas/CompletedSetDto"}},"rpeAvg":{"type":["number","null"]},"restTimeAvgSeconds":{"type":["number","null"]},"notes":{"type":["string","null"]},"section":{"anyOf":[{"$ref":"#/components/schemas/WorkoutSection"},{"type":"null"}]},"restAfterSeconds":{"type":["number","null"]},"templateNotes":{"type":["string","null"]},"showRpe":{"type":["boolean","null"]},"previewLoops":{"type":["boolean","null"]},"groupId":{"type":["string","null"]},"groupFormat":{"anyOf":[{"$ref":"#/components/schemas/ExerciseGroupFormat"},{"type":"null"}]},"groupName":{"type":["string","null"]},"groupRounds":{"type":["number","null"]},"groupRestBetweenRoundsSeconds":{"type":["number","null"]},"groupTimeCapSeconds":{"type":["number","null"]},"groupNotes":{"type":["string","null"]},"groupOrder":{"type":["number","null"]},"groupSize":{"type":["number","null"]}},"required":["exerciseId","order","units","completedSets"],"additionalProperties":false},"SetDataType":{"type":"string","enum":["reps","kg","lbs","%","bodyweight","sec","min","m","yd"],"description":"SetDataType serializes via"},"CompletedSetDto":{"type":"object","properties":{"setNumber":{"type":"number"},"values":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}]},"results":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"rangeHints":{"anyOf":[{"type":"array","items":{"type":["string","null"]}},{"type":"null"}]},"rpe":{"type":["number","null"]},"completed":{"type":["boolean","null"]}},"required":["setNumber"],"additionalProperties":false},"WorkoutSection":{"type":"string","enum":["WARMUP","MAIN","FINISHER","COOLDOWN"]},"ExerciseGroupFormat":{"type":"string","enum":["REGULAR","SUPERSET","DROPSET","CIRCUIT","AMRAP","EMOM","TABATA","HYROX_STYLE","FOR_TIME","STRENGTH_FOCUSED","LADDER","CHIPPER"]},"CreatedAgentConnectionDto":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":["string","null"]},"tier":{"$ref":"#/components/schemas/AgentScopeTier"},"lastUsedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"revokedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"createdAt":{"type":"string","format":"date-time"},"rawKey":{"type":"string"}},"required":["createdAt","id","label","lastUsedAt","rawKey","revokedAt","tier"],"additionalProperties":false,"description":"Create response: the safe view plus the raw key, returned exactly once."},"CreditBalanceResponse":{"type":"object","properties":{"aiCreditsUsed":{"type":"number"},"aiCreditsIncluded":{"type":"number"},"overageCreditPacks":{"type":"number"},"estimatedOverageCost":{"type":"number"},"aiCreditOverageEnabled":{"type":"boolean"}},"required":["aiCreditsUsed","aiCreditsIncluded","overageCreditPacks","estimatedOverageCost","aiCreditOverageEnabled"],"additionalProperties":false,"description":"Response DTO for AI credit balance."},"CreditOverageToggleRequest":{"type":"object","properties":{"enabled":{"type":"boolean"}},"required":["enabled"],"additionalProperties":false,"description":"Request DTO for toggling AI credit overage."},"CreditOverageToggleResponse":{"type":"object","properties":{"aiCreditOverageEnabled":{"type":"boolean"}},"required":["aiCreditOverageEnabled"],"additionalProperties":false,"description":"Response DTO for AI credit overage toggle."},"CreditTransactionDto":{"type":"object","properties":{"id":{"type":"string"},"amount":{"type":"number"},"balanceAfter":{"type":"number"},"transactionType":{"anyOf":[{"$ref":"#/components/schemas/CreditTransactionType"},{"type":"null"}]},"description":{"type":["string","null"]},"referenceId":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","amount","balanceAfter","transactionType","description","referenceId","createdAt"],"additionalProperties":false,"description":"DTO for AI credit transaction log entries."},"CreditTransactionType":{"type":"string","enum":["CONSUMPTION","MONTHLY_RESET","ADMIN_ADJUSTMENT"]},"DailyNutritionSummaryDto":{"type":"object","properties":{"date":{"type":"string"},"userId":{"type":"string"},"totalNutrients":{"$ref":"#/components/schemas/NutrientsInfo"},"logs":{"type":"array","items":{"$ref":"#/components/schemas/NutritionLogDto"}},"logCount":{"type":"number"},"templateComparison":{"anyOf":[{"$ref":"#/components/schemas/TemplateComparisonDto"},{"type":"null"}]}},"required":["date","userId","totalNutrients","logs","logCount","templateComparison"],"additionalProperties":false},"NutritionLogDto":{"type":"object","properties":{"id":{"type":"string"},"userId":{"type":"string"},"loggedBy":{"type":["string","null"]},"logDate":{"type":"string"},"logTime":{"type":["string","null"]},"inputType":{"$ref":"#/components/schemas/NutritionLogInputType"},"originalInput":{"type":["string","null"]},"image":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"items":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/NutritionLogItem"}},{"type":"null"}]},"totalNutrients":{"anyOf":[{"$ref":"#/components/schemas/NutrientsInfo"},{"type":"null"}]},"notes":{"type":["string","null"]},"nutritionTemplateId":{"type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["id","userId","loggedBy","logDate","logTime","inputType","originalInput","image","items","totalNutrients","notes","nutritionTemplateId","createdAt","updatedAt"],"additionalProperties":false},"TemplateComparisonDto":{"type":"object","properties":{"templateId":{"type":"string"},"templateName":{"type":["string","null"]},"targetNutrients":{"$ref":"#/components/schemas/NutrientsInfo"},"consumedNutrients":{"$ref":"#/components/schemas/NutrientsInfo"},"remainingNutrients":{"$ref":"#/components/schemas/NutrientsInfo"},"percentageComplete":{"$ref":"#/components/schemas/NutrientPercentages"}},"required":["templateId","templateName","targetNutrients","consumedNutrients","remainingNutrients","percentageComplete"],"additionalProperties":false},"NutrientPercentages":{"type":"object","properties":{"calories":{"type":["number","null"]},"protein":{"type":["number","null"]},"carbs":{"type":["number","null"]},"fat":{"type":["number","null"]},"fiber":{"type":["number","null"]}},"required":["calories","protein","carbs","fat","fiber"],"additionalProperties":false},"DashboardOverviewDto":{"type":"object","properties":{"activeClientsCount":{"type":"number"},"newClientsThisMonth":{"type":"number"},"churnedClientsCount":{"type":"number"},"programsActive":{"type":"number"},"programsExpiringIn7Days":{"type":"number"},"programsExpired":{"type":"number"},"progressEntriesPendingReview":{"type":"number"},"clientsByLifecycleStage":{"type":"array","items":{"$ref":"#/components/schemas/ClientsByStageEntry"}},"progressEntriesPerWeek":{"type":"array","items":{"$ref":"#/components/schemas/ProgressEntriesPerWeek"}}},"required":["activeClientsCount","newClientsThisMonth","churnedClientsCount","programsActive","programsExpiringIn7Days","programsExpired","progressEntriesPendingReview","clientsByLifecycleStage","progressEntriesPerWeek"],"additionalProperties":false},"ProgressEntriesPerWeek":{"type":"object","properties":{"weekStart":{"type":"string"},"count":{"type":"number"}},"required":["weekStart","count"],"additionalProperties":false},"DescribeMealImageDto":{"type":"object","properties":{"media":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"language":{"type":["string","null"],"description":"Language code or name the description should be written in (default English)."}},"additionalProperties":false,"description":"ECS-1104 image-prefill flow: the client's photo is turned into an EDITABLE text description (in the client's language) instead of being analyzed directly; the confirmed text then goes through the standard TEXT analyze route."},"DraftDto":{"type":"object","properties":{"conversationId":{"type":"string"},"content":{"type":"string"},"entities":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/MessageEntity"}},{"type":"null"}]},"media":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/MediaDto"}},{"type":"null"}]},"contentFormat":{"type":"number"},"updatedById":{"type":["string","null"]},"updatedByName":{"type":["string","null"]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["conversationId","content","entities","media","contentFormat","updatedById","updatedByName","updatedAt"],"additionalProperties":false,"description":"Full draft, returned by GET/PUT /draft and loaded into the composer on open."},"MessageEntity":{"type":"object","properties":{"type":{"$ref":"#/components/schemas/MessageEntityType"},"offset":{"type":"number","description":"UTF-16 code-unit offset into `content`."},"length":{"type":"number","description":"UTF-16 code-unit length."},"url":{"type":"string","description":"`text_link` only — the destination URL."},"userId":{"type":"string","description":"`mention` only — the referenced user id."},"language":{"type":"string","description":"`pre` only — optional language hint for the code block."}},"required":["type","offset","length"],"additionalProperties":false},"MessageEntityType":{"type":"string","enum":["bold","italic","strikethrough","code","pre","text_link","mention"],"description":"Canonical message-formatting model — Telegram-style entity ranges.\n\nA message is `{ content: string, entities: MessageEntity[] }`. Offsets and lengths are measured in UTF-16 code units (matching JS string indexing and the Telegram Bot API), so a surrogate-pair emoji counts as length 2. A message with no entities is plain text.\n\nThis module is PURE (no I/O, no framework, no runtime deps) and is the single source of truth for formatting across api, web, and mobile."},"DraftUpsertBody":{"type":"object","properties":{"content":{"type":"string"},"entities":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/MessageEntity"}},{"type":"null"}]},"media":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/MediaDto"}},{"type":"null"}]},"contentFormat":{"type":"number"}},"additionalProperties":false,"description":"Request body for PUT /draft."},"ExerciseDto":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"createdBy":{"type":"string"},"name":{"type":"string"},"description":{"$ref":"#/components/schemas/MediaDto"},"difficultyLevel":{"$ref":"#/components/schemas/ExperienceLevel"},"exerciseType":{"$ref":"#/components/schemas/ExerciseType"},"status":{"$ref":"#/components/schemas/ExerciseStatus"},"video":{"$ref":"#/components/schemas/MediaDto"},"image":{"$ref":"#/components/schemas/MediaDto"},"noImage":{"type":"boolean"},"noVideo":{"type":"boolean"},"additionalMedia":{"type":"array","items":{"$ref":"#/components/schemas/MediaDto"}},"isCompound":{"type":"boolean"},"riskLevel":{"type":"number"},"primaryMuscleGroups":{"type":"array","items":{"$ref":"#/components/schemas/MuscleGroup"}},"equipmentRequired":{"type":"array","items":{"$ref":"#/components/schemas/Equipment"}},"equipmentAssets":{"type":"array","items":{"$ref":"#/components/schemas/EquipmentAsset"}},"gender":{"$ref":"#/components/schemas/Gender"},"performanceTargets":{"$ref":"#/components/schemas/Record<string,PerformanceTarget>"},"isPublicLibrary":{"type":"boolean"},"alternatives":{"type":"array","items":{"type":"string"}},"translations":{"$ref":"#/components/schemas/Record<string,ExerciseTranslations>"},"ownerId":{"type":["string","null"]},"visibility":{"anyOf":[{"$ref":"#/components/schemas/ResourceVisibility"},{"type":"null"}],"description":"Explicit PRIVATE/TEAM share marker (TEAM-mode tenants). On output always present (response contract); on input only applied when explicitly set — sharing changes are owner/admin-gated in TEAM mode."},"externalId":{"type":"string"},"externalUpdatedAt":{"type":"string","format":"date-time"},"categoryId":{"type":"string"},"looping":{"type":"boolean"},"soundTracks":{"type":"array","items":{"$ref":"#/components/schemas/MediaDto"}}},"required":["isPublicLibrary"],"additionalProperties":false,"description":"Ports com.protocolcrm.api.dto.workout.ExerciseDto and the exercise filter response.\n\nCONTRACT:  - ExerciseDto IS"},"ExperienceLevel":{"type":"string","enum":["BEGINNER","INTERMEDIATE","ADVANCED","ELITE"]},"ExerciseType":{"type":"string","enum":["STRENGTH","CARDIO","FLEXIBILITY","BALANCE","PLYOMETRIC","FUNCTIONAL","STRETCHING"],"description":"Shared workout enums and JSONB sub-objects.\n\nThese mirror the Kotlin model types in com.protocolcrm.api.model.workout.* and com.protocolcrm.api.dto.workout.*. The load-bearing thing is the string values (enums stored as varchar via"},"ExerciseStatus":{"type":"string","enum":["DRAFT","ACTIVE","DEPRECATED"]},"MuscleGroup":{"type":"string","enum":["CHEST","BACK","STRETCHING","FOREARMS","SHOULDERS","CARDIO","CALVES","FACE","FEET","HANDS","HIPS","NECK","PLYOMETRICS","THIGHS","UPPER_ARMS","WAIST","WEIGHTLIFTING","YOGA"]},"Equipment":{"type":"string","enum":["NONE","MINIMAL","HOME_GYM","FULL_GYM"]},"EquipmentAsset":{"type":"string"},"Gender":{"type":"string","enum":["MALE","FEMALE","OTHER","PREFER_NOT_TO_SAY"]},"Record<string,PerformanceTarget>":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/PerformanceTarget"}},"PerformanceTarget":{"type":"object","properties":{"targetRepsMin":{"type":["number","null"]},"targetRepsMax":{"type":["number","null"]},"targetSets":{"type":["number","null"]},"restTimeSeconds":{"type":["number","null"]},"tempoCode":{"type":["string","null"]},"rpeTarget":{"type":["number","null"]}},"additionalProperties":false},"Record<string,ExerciseTranslations>":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ExerciseTranslations"}},"ExerciseTranslations":{"type":"object","additionalProperties":{}},"ExerciseFiltersResponseDto":{"type":"object","properties":{"exerciseTypes":{"type":"array","items":{"type":"string"}},"difficultyLevels":{"type":"array","items":{"type":"string"}},"muscleGroups":{"type":"array","items":{"type":"string"}},"equipmentOptions":{"type":"array","items":{"type":"string"}},"statusOptions":{"type":"array","items":{"type":"string"}}},"required":["exerciseTypes","difficultyLevels","muscleGroups","equipmentOptions","statusOptions"],"additionalProperties":false},"ExerciseHistoryDto":{"type":"object","properties":{"id":{"type":"string"},"exerciseId":{"type":"string"},"exerciseName":{"type":"string"},"sessionId":{"type":"string"},"workoutId":{"type":"string"},"workoutName":{"type":"string"},"completedAt":{"type":"string","format":"date-time"},"units":{"type":"array","items":{"$ref":"#/components/schemas/SetDataType"}},"completedSets":{"type":"array","items":{"$ref":"#/components/schemas/CompletedSetDto"}},"rpeAvg":{"type":"number"},"restTimeAvgSeconds":{"type":"number"},"notes":{"type":"string"}},"required":["id","exerciseId","sessionId","completedAt","units","completedSets"],"additionalProperties":false},"ExpiringPurchaseDto":{"type":"object","properties":{"purchaseId":{"type":"string"},"productId":{"type":["string","null"]},"productName":{"type":["string","null"]},"userId":{"type":["string","null"]},"userName":{"type":["string","null"]},"pricingType":{"anyOf":[{"$ref":"#/components/schemas/PricingType"},{"type":"null"}]},"expiresAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"amount":{"type":["number","null"]},"currency":{"type":["string","null"]},"status":{"anyOf":[{"$ref":"#/components/schemas/PurchaseStatus"},{"type":"null"}]}},"required":["purchaseId"],"additionalProperties":false,"description":"ExpiringPurchaseDto (@JsonInclude(NON_NULL))."},"PricingType":{"type":"string","enum":["ONE_TIME","SUBSCRIPTION"],"description":"Port of com.protocolcrm.api.model.shop.ShopEnums.kt.\n\nEvery enum that is persisted on a shop entity is"},"PurchaseStatus":{"type":"string","enum":["PENDING","ACTIVE","PAST_DUE","PAUSED","CANCELED","EXPIRED","REFUNDED","FAILED"],"description":"Unified purchase status (ShopEnums.PurchaseStatus)."},"FeedbackDto":{"type":"object","properties":{"id":{"type":["string","null"]},"subject":{"type":["string","null"]},"message":{"type":["string","null"]},"context":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]},"userId":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","subject","message","context","userId","createdAt"],"additionalProperties":false,"description":"Mirrors FeedbackDto (no"},"FindAlternativesRequestDto":{"type":"object","properties":{"exerciseId":{"type":"string"},"equipmentAvailable":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]}},"additionalProperties":false,"description":"Request body for POST /:exerciseId/alternatives (optional)."},"FormAction":{"type":"object","properties":{"action":{"$ref":"#/components/schemas/FormActionType"}},"required":["action"],"additionalProperties":false},"FormActionType":{"type":"string","enum":["CREATE_CLIENT","AUTO_ONBOARD","SEND_PDF_HOOK"]},"FormAnswerDto":{"type":"object","properties":{"questionTitle":{"type":"string"},"answer":{},"questionId":{"type":"string"},"questionDescription":{"type":["string","null"]},"mapTo":{"anyOf":[{"$ref":"#/components/schemas/CheckInMapTo"},{"type":"null"}]},"customMapTo":{"type":["string","null"]},"customPurpose":{"type":["string","null"]}},"required":["questionTitle","answer","questionId"],"additionalProperties":false},"FormFilters":{"type":"object","properties":{"searchTerm":{"type":"string"},"title":{"type":"string"},"presentationType":{"type":"array","items":{"$ref":"#/components/schemas/FormPresentationType"}},"createdAfter":{"type":"string","format":"date-time"},"createdBefore":{"type":"string","format":"date-time"}},"additionalProperties":false},"FormPresentationType":{"type":"string","enum":["SINGLE_PAGE","MULTI_PAGE","HABIT_TRACKING","PROGRESS_TRACKING"],"description":"Ports the marketing form DTOs/models that cross the HTTP boundary:   - com.protocolcrm.api.dto.FormDTO.kt (request/response DTOs)   - com.protocolcrm.api.model.marketing.FormTypes.kt (Question/Theme/etc + enums)   - com.protocolcrm.api.service.marketing.FormTemplateDto / FormTemplateCategory"},"FormListParams":{"type":"object","properties":{"page":{"type":"number"},"pageSize":{"type":"number"},"sortBy":{"type":"string"},"sortDir":{"type":"string","enum":["ASC","DESC"]},"filters":{"anyOf":[{"$ref":"#/components/schemas/FormFilters"},{"type":"null"}]}},"required":["page","pageSize","sortBy","sortDir","filters"],"additionalProperties":false},"FormRequestDto":{"type":"object","properties":{"title":{"type":["string","null"]},"description":{"type":["string","null"]},"presentationType":{"anyOf":[{"$ref":"#/components/schemas/FormPresentationType"},{"type":"null"}]},"questions":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/Question"}},{"type":"null"}]},"theme":{"anyOf":[{"$ref":"#/components/schemas/Theme"},{"type":"null"}]},"settings":{"anyOf":[{"$ref":"#/components/schemas/FormSettings"},{"type":"null"}]},"translations":{"anyOf":[{"$ref":"#/components/schemas/Record<string,FormTranslations>"},{"type":"null"}]}},"additionalProperties":false},"Question":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"purpose":{"type":"string"},"buttonText":{"type":"string"},"placeholder":{"type":"string"},"required":{"type":"boolean"},"options":{"type":"array","items":{"$ref":"#/components/schemas/Option"}},"allowMultiple":{"type":"boolean"},"allowedTypes":{"type":"array","items":{"type":"string"}},"termsText":{"type":"string"},"redirectUrl":{"type":"string"},"redirectWaitSeconds":{"type":"number"},"collectEmail":{"type":"boolean"},"emailPlaceholder":{"type":"string"},"layout":{"type":"string"},"theme":{"$ref":"#/components/schemas/Theme"},"maxRating":{"type":"number"},"ratingLabel":{"type":"string"},"videoMedia":{"$ref":"#/components/schemas/MediaDto"},"requireSignature":{"type":"boolean"},"mapTo":{"$ref":"#/components/schemas/CheckInMapTo"},"customMapTo":{"type":"string"},"customPurpose":{"type":"string"},"pinned":{"type":"boolean"},"translations":{"$ref":"#/components/schemas/Record<string,QuestionTranslations>"}},"additionalProperties":false},"Option":{"type":"object","properties":{"id":{"type":"string"},"label":{"type":"string"},"image":{"type":"string","deprecated":"use imageMedia"},"imageMedia":{"$ref":"#/components/schemas/MediaDto"},"translations":{"$ref":"#/components/schemas/Record<string,OptionTranslations>"}},"additionalProperties":false},"Record<string,OptionTranslations>":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/OptionTranslations"}},"OptionTranslations":{"$ref":"#/components/schemas/Record<string,unknown>"},"Theme":{"type":"object","properties":{"preset":{"type":"string"},"palette":{"$ref":"#/components/schemas/Palette"},"typography":{"$ref":"#/components/schemas/Typography"},"shape":{"$ref":"#/components/schemas/Shape"},"backgroundMedia":{"$ref":"#/components/schemas/MediaDto"}},"additionalProperties":false},"Palette":{"type":"object","properties":{"mode":{"type":"string"},"primary":{"type":"string"},"secondary":{"type":"string"},"contrastText":{"type":"string"},"background":{"type":"string"},"questionBackground":{"type":"string"},"textPrimary":{"type":"string"},"textSecondary":{"type":"string"},"textDisabled":{"type":"string"}},"additionalProperties":false},"Typography":{"type":"object","properties":{"fontFamily":{"type":"string"},"fontSize":{"type":"number"},"heading":{"$ref":"#/components/schemas/TypographyStyle"},"question":{"$ref":"#/components/schemas/TypographyStyle"},"body":{"$ref":"#/components/schemas/TypographyStyle"},"button":{"$ref":"#/components/schemas/TypographyStyle"}},"additionalProperties":false},"Shape":{"type":"object","properties":{"borderRadius":{"type":"number"},"questionSpacing":{"type":"number"},"questionBorderStyle":{"type":"string"},"formMinHeight":{"type":"string"}},"additionalProperties":false},"Record<string,QuestionTranslations>":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/QuestionTranslations"}},"QuestionTranslations":{"$ref":"#/components/schemas/Record<string,unknown>"},"FormSettings":{"type":"object","properties":{"showProgressBar":{"type":"boolean"},"onSubmitActions":{"type":"array","items":{"$ref":"#/components/schemas/FormAction"}},"notifyTrainerOnSubmit":{"type":"boolean"},"notifySubmitterOnSubmit":{"type":"boolean"},"specialPurpose":{"$ref":"#/components/schemas/FormSpecialPurpose"},"internalNotifyOnSubmit":{"type":"array","items":{"type":"string"}},"aiPromptTemplate":{"type":"string"},"aiOutputFormat":{"$ref":"#/components/schemas/AiOutputFormat"}},"additionalProperties":false},"FormSpecialPurpose":{"type":"string","enum":["CHECK_IN","INITIAL_QUESTIONNAIRE","SURVEY","OTHER"]},"AiOutputFormat":{"type":"string","enum":["TEXT","HTML","MARKDOWN"]},"Record<string,FormTranslations>":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/FormTranslations"}},"FormTranslations":{"$ref":"#/components/schemas/Record<string,unknown>"},"FormResponseDto":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":["string","null"]},"description":{"type":["string","null"]},"version":{"type":["number","null"]},"presentationType":{"type":["string","null"]},"questions":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/Question"}},{"type":"null"}]},"theme":{"anyOf":[{"$ref":"#/components/schemas/Theme"},{"type":"null"}]},"settings":{"anyOf":[{"$ref":"#/components/schemas/FormSettings"},{"type":"null"}]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"translations":{"anyOf":[{"$ref":"#/components/schemas/Record<string,FormTranslations>"},{"type":"null"}]}},"required":["id","title","description","version","presentationType","questions","theme","settings","createdAt","updatedAt","translations"],"additionalProperties":false},"FormSubmissionFilters":{"type":"object","properties":{"formId":{"type":"string"},"userId":{"type":"string"},"email":{"type":"string"},"leadId":{"type":"string"},"createdAfter":{"type":"string","format":"date-time"},"createdBefore":{"type":"string","format":"date-time"},"specialPurpose":{"type":"string"}},"additionalProperties":false},"FormSubmissionListParams":{"type":"object","properties":{"page":{"type":"number"},"pageSize":{"type":"number"},"sortBy":{"type":"string"},"sortDir":{"type":"string","enum":["ASC","DESC"]},"filters":{"$ref":"#/components/schemas/FormSubmissionFilters"}},"required":["page","pageSize","sortBy","sortDir","filters"],"additionalProperties":false,"description":"Mirrors GET /formSubmissions"},"FormSubmissionResponseDto":{"type":"object","properties":{"id":{"type":"string"},"formId":{"type":"string"},"answers":{"type":"array","items":{"$ref":"#/components/schemas/FormAnswerDto"}},"email":{"type":["string","null"]},"phone":{"type":["string","null"]},"action":{"type":["string","null"]},"userId":{"type":["string","null"]},"leadId":{"type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"computedOutput":{"type":["string","null"]},"computedOutputFormat":{"type":["string","null"]},"specialPurpose":{"type":["string","null"],"description":"Snapshot of the form's purpose at submit time (FormSpecialPurpose name)."}},"required":["id","formId","answers","email","phone","action","userId","leadId","createdAt","updatedAt","computedOutput","computedOutputFormat","specialPurpose"],"additionalProperties":false},"FormSubmissionsPageParams":{"type":"object","properties":{"page":{"type":"number"},"pageSize":{"type":"number"},"sortBy":{"type":"string"},"sortDir":{"type":"string","enum":["ASC","DESC"]}},"required":["page","pageSize","sortBy","sortDir"],"additionalProperties":false,"description":"Mirrors the deprecated GET /forms/{id}/submissions"},"FormSubmitRequestDto":{"type":"object","properties":{"formId":{"type":"string"},"answers":{"type":"array","items":{"$ref":"#/components/schemas/FormAnswerDto"}},"email":{"type":["string","null"]},"phone":{"type":["string","null"]},"action":{"type":["string","null"]},"userId":{"type":["string","null"]},"submittedAt":{"type":["string","null"]}},"required":["formId","answers"],"additionalProperties":false},"FormSubmitResponseDto":{"type":"object","properties":{"computedOutput":{"type":"string"},"outputFormat":{"$ref":"#/components/schemas/AiOutputFormat"}},"additionalProperties":false},"FormTemplateDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string"},"category":{"$ref":"#/components/schemas/FormTemplateCategory"},"form":{"$ref":"#/components/schemas/FormRequestDto"}},"required":["id","name","description","category","form"],"additionalProperties":false},"FormTemplateCategory":{"type":"string","enum":["CLIENT_ASSESSMENT","PROGRESS_TRACKING","HABIT_TRACKING","CONSULTATION","FEEDBACK"]},"FormUpdateRequestDto":{"type":"object","properties":{"title":{"type":["string","null"]},"description":{"type":["string","null"]},"presentationType":{"anyOf":[{"$ref":"#/components/schemas/FormPresentationType"},{"type":"null"}]},"questions":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/Question"}},{"type":"null"}]},"theme":{"anyOf":[{"$ref":"#/components/schemas/Theme"},{"type":"null"}]},"settings":{"anyOf":[{"$ref":"#/components/schemas/FormSettings"},{"type":"null"}]},"translations":{"anyOf":[{"$ref":"#/components/schemas/Record<string,FormTranslations>"},{"type":"null"}]}},"additionalProperties":false},"ForwardMessageRequestDto":{"type":"object","properties":{"conversationIds":{"type":"array","items":{"type":"string"}},"forwardMessage":{"type":["string","null"]}},"required":["conversationIds"],"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.conversation.ForwardMessageRequestDto."},"GenerateClientCodeRequestDto":{"type":"object","properties":{"clientId":{"type":"string"},"durationHours":{"type":"number"}},"required":["clientId"],"additionalProperties":false,"description":"Mirrors GenerateClientCodeRequestDto."},"GenerateExerciseRequestDto":{"type":"object","properties":{"descriptionQuery":{"type":"string"},"currentExercise":{"anyOf":[{"$ref":"#/components/schemas/ExerciseDto"},{"type":"null"}]}},"required":["descriptionQuery"],"additionalProperties":false,"description":"Request DTO for generating an exercise from AI (GenerateExerciseRequestDto)."},"GenerateExerciseResponseDto":{"type":"object","properties":{"response":{"type":"string"},"generatedExercise":{"$ref":"#/components/schemas/ExerciseDto"}},"additionalProperties":false,"description":"Response DTO for AI exercise generation (GenerateExerciseResponseDto,"},"GenerateFormRequestDto":{"type":"object","properties":{"currentFormSetup":{"$ref":"#/components/schemas/FormRequestDto"},"changeQuery":{"type":"string"}},"required":["currentFormSetup","changeQuery"],"additionalProperties":false},"GenerateFormResponseDto":{"type":"object","properties":{"updatedForm":{"anyOf":[{"$ref":"#/components/schemas/FormRequestDto"},{"type":"null"}]},"response":{"type":"string"}},"required":["response"],"additionalProperties":false},"GenerateImageRequestDto":{"type":"object","properties":{"prompt":{"type":"string"}},"required":["prompt"],"additionalProperties":false,"description":"Mirrors GenerateImageRequestDto."},"GenerateJsonRequestDto":{"type":"object","properties":{"instruction":{"type":"string"},"responseFormat":{"$ref":"#/components/schemas/Record<string,unknown>"},"effort":{"anyOf":[{"$ref":"#/components/schemas/AiEffort"},{"type":"null"}]}},"required":["instruction","responseFormat"],"additionalProperties":false,"description":"Mirrors GenerateJsonRequestDto."},"AiEffort":{"type":"string","enum":["NANO","LIGHT","HEAVY"],"description":"Mirrors com.protocolcrm.api.dto.util.AiEffort."},"GenerateJsonResponseDto":{"type":"object","properties":{"data":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]}},"required":["data"],"additionalProperties":false,"description":"Mirrors GenerateJsonResponseDto."},"GenerateMarkdownRequestDto":{"type":"object","properties":{"instruction":{"type":"string"},"effort":{"anyOf":[{"$ref":"#/components/schemas/AiEffort"},{"type":"null"}]}},"required":["instruction"],"additionalProperties":false,"description":"Mirrors GenerateMarkdownRequestDto."},"GenerateMarkdownResponseDto":{"type":"object","properties":{"markdown":{"type":["string","null"]}},"required":["markdown"],"additionalProperties":false,"description":"Mirrors GenerateMarkdownResponseDto."},"GenomicInsightRequestDto":{"type":"object","properties":{"conditionName":{"type":"string"},"category":{"type":"string"},"resultState":{"$ref":"#/components/schemas/GenomicInsightResultState"},"mainMessage":{"type":"string"},"gene":{"type":["string","null"]},"rsId":{"type":["string","null"]},"genotype":{"type":["string","null"]},"additionalMessage":{"type":["string","null"]}},"required":["conditionName","category","resultState","mainMessage"],"additionalProperties":false},"GenomicInsightResultState":{"type":"string","enum":["NORMAL","ENHANCED","FLAGGED","UNKNOWN"]},"GenomicInsightResponseDto":{"type":"object","properties":{"id":{"type":["string","null"]},"conditionName":{"type":["string","null"]},"category":{"type":["string","null"]},"resultState":{"anyOf":[{"$ref":"#/components/schemas/GenomicInsightResultState"},{"type":"null"}]},"mainMessage":{"type":["string","null"]},"gene":{"type":["string","null"]},"rsId":{"type":["string","null"]},"genotype":{"type":["string","null"]},"additionalMessage":{"type":["string","null"]}},"additionalProperties":false},"GenomicProfileRequestDto":{"type":"object","properties":{"overallAssessment":{"type":["string","null"]},"testProvider":{"type":["string","null"]},"testDate":{"type":["string","null"]},"genomicInsights":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/GenomicInsightRequestDto"}},{"type":"null"}]}},"additionalProperties":false},"GenomicProfileResponseDto":{"type":"object","properties":{"id":{"type":["string","null"]},"overallAssessment":{"type":["string","null"]},"testProvider":{"type":["string","null"]},"testDate":{"type":["string","null"]},"genomicInsights":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/GenomicInsightResponseDto"}},{"type":"null"}]},"lastUpdatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"additionalProperties":false},"HabitLogDto":{"type":"object","properties":{"id":{"type":"string"},"habitType":{"$ref":"#/components/schemas/HabitType"},"userId":{"type":"string"},"logDate":{"type":"string"},"completed":{"type":"boolean"},"value":{"type":"number"},"notes":{"type":"string"},"source":{"$ref":"#/components/schemas/MeasurementSource"},"loggedAt":{"type":"string","format":"date-time"}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.habit.HabitLogDto (@JsonInclude(NON_NULL)) — null fields are stripped from the response (see stripNullish / toHabitLogDto).\n\n`loggedAt` is a java.time.Instant on the entity → serialized as an ISO-8601 instant (TypeORM returns a Date; surfaced as a Date object, matching the other ported DTOs). `logDate` is a PG `date` → 'YYYY-MM-DD' string."},"MeasurementSource":{"type":"string","enum":["MANUAL","DEVICE","INTEGRATION","ASSESSMENT"]},"HabitLogFilters":{"type":"object","properties":{"userId":{"type":"string"},"habitType":{"$ref":"#/components/schemas/HabitType"},"startDate":{"type":"string"},"endDate":{"type":"string"},"completed":{"type":"boolean"}},"additionalProperties":false},"HabitLogListParams":{"type":"object","properties":{"page":{"type":"number"},"pageSize":{"type":"number"},"sortBy":{"type":"string"},"sortDir":{"type":"string","enum":["ASC","DESC"]},"filters":{"$ref":"#/components/schemas/HabitLogFilters"}},"required":["page","pageSize","sortBy","sortDir","filters"],"additionalProperties":false},"HabitLogRequestDto":{"type":"object","properties":{"habitType":{"$ref":"#/components/schemas/HabitType"},"userId":{"type":"string"},"logDate":{"type":"string"},"completed":{"type":"boolean"},"value":{"type":"number"},"notes":{"type":"string"},"source":{"$ref":"#/components/schemas/MeasurementSource"}},"additionalProperties":false,"description":"Accepted request body for POST/PUT (logHabit / updateLog)."},"IncompleteProfileDto":{"type":"object","properties":{"profileCode":{"type":"string"},"incompleteFields":{"type":"array","items":{"type":"string"}},"completionPercentage":{"type":"number"}},"required":["profileCode","incompleteFields","completionPercentage"],"additionalProperties":false,"description":"No"},"IncompleteProfilesResponseDto":{"type":"object","properties":{"incompleteProfiles":{"type":"array","items":{"$ref":"#/components/schemas/IncompleteProfileDto"}}},"required":["incompleteProfiles"],"additionalProperties":false,"description":"No"},"InvoiceDto":{"type":"object","properties":{"id":{"type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"purchaseId":{"type":["string","null"]},"invoiceNumber":{"type":["string","null"]},"amount":{"type":["number","null"]},"currency":{"type":["string","null"]},"taxAmount":{"type":["number","null"]},"status":{"anyOf":[{"$ref":"#/components/schemas/InvoiceStatus"},{"type":"null"}]},"issuedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"paidAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"dueAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"periodStart":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"periodEnd":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"stripeInvoiceId":{"type":["string","null"]},"billingEmail":{"type":["string","null"]},"billingName":{"type":["string","null"]},"lineItems":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/InvoiceLineItem"}},{"type":"null"}]},"metadata":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.shop.InvoiceDto (@JsonInclude(NON_NULL)). Base id/createdAt/updatedAt first, then InvoiceDto fields, in Kotlin declaration order."},"InvoiceStatus":{"type":"string","enum":["DRAFT","ISSUED","PAID","VOID"]},"InvoiceLineItem":{"type":"object","properties":{"name":{"type":["string","null"]},"quantity":{"type":["number","null"]},"unitPrice":{"type":["number","null"]},"amount":{"type":["number","null"]},"taxRateId":{"type":["string","null"]},"taxRateName":{"type":["string","null"]},"taxRateValue":{"type":["number","null"]},"taxInclusive":{"type":["boolean","null"]},"taxAmount":{"type":["number","null"]}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.model.shop.InvoiceLineItem (jsonb element shape). unitPrice/amount/taxAmount are cents (Long); taxRateValue is a BigDecimal (number in JSON)."},"IpAccessInfo":{"type":"object","properties":{"ipAddress":{"type":"string"},"lastAccessedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"isActive":{"type":"boolean"}},"required":["ipAddress","lastAccessedAt","isActive"],"additionalProperties":false},"IpAccessListResponse":{"type":"object","properties":{"code":{"type":"string"},"maxAllowed":{"type":"number"},"activeCount":{"type":"number"},"totalCount":{"type":"number"},"ipAccesses":{"type":"array","items":{"$ref":"#/components/schemas/IpAccessInfo"}}},"required":["code","maxAllowed","activeCount","totalCount","ipAccesses"],"additionalProperties":false},"KindMetadataDto":{"type":"object","properties":{"kind":{"$ref":"#/components/schemas/AutomationKind"},"displayName":{"type":"string"},"supportedTriggers":{"type":"array","items":{"$ref":"#/components/schemas/AutomationTriggerType"}}},"required":["kind","displayName","supportedTriggers"],"additionalProperties":false},"AutomationTriggerType":{"type":"string","enum":["PROGRESS_ENTRY_CREATED","FORM_SUBMITTED","LEAD_CREATED","BOOKING_CREATED","APPOINTMENT_CREATED","MANUAL"]},"LabelDto":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"tenantId":{"type":"string"},"sector":{"$ref":"#/components/schemas/LabelSector"},"name":{"type":"string"},"color":{"type":"string"},"displayOrder":{"type":"number"},"active":{"type":"boolean"}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.user.LabelDto (@JsonInclude(NON_NULL)). Field order matches the Kotlin data class primary constructor (id, createdAt, updatedAt, tenantId, sector, name, color, displayOrder, active). NON_NULL → null/undefined keys are stripped (see toLabelDto / stripNullish)."},"LabelSector":{"type":"string","const":"PROGRESS_ENTRY","description":"Mirrors com.protocolcrm.api.model.user.LabelSector."},"LabelUpsertDto":{"type":"object","properties":{"id":{"type":["string","null"]},"name":{"type":"string"},"color":{"type":"string"},"displayOrder":{"type":"number"},"active":{"type":"boolean"}},"required":["name"],"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.user.LabelUpsertDto. `name` is required; the rest carry the Kotlin defaults when omitted."},"LifecycleStageDto":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"tenantId":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"position":{"type":"number"},"isTerminal":{"type":"boolean"}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.user.LifecycleStageDto (@JsonInclude(NON_NULL)). Field order matches the Kotlin data class (id, createdAt, updatedAt, tenantId, name, color, position, isTerminal). NON_NULL → null/undefined keys are stripped (see toLifecycleStageDto / stripNullish). `position`/`isTerminal` are non-null primitives in Kotlin so they are always emitted."},"LifecycleStageReorderRequestDto":{"type":"object","properties":{"orderedIds":{"type":"array","items":{"type":"string"}}},"required":["orderedIds"],"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.user.LifecycleStageReorderRequestDto."},"LoginAsRequestDto":{"type":"object","properties":{"userId":{"type":"string"}},"required":["userId"],"additionalProperties":false,"description":"Mirrors LoginAsRequestDto."},"LoginRequestDto":{"type":"object","properties":{"email":{"type":"string"},"password":{"type":"string"},"code":{"type":"string"},"tenantId":{"type":"string"},"requestAuthCode":{"type":"boolean"}},"required":["email"],"additionalProperties":false},"ManualRunRequest":{"type":"object","properties":{"triggerData":{"$ref":"#/components/schemas/Record<string,unknown>"}},"additionalProperties":false},"ManualRunResponse":{"type":"object","properties":{"runId":{"type":"string"},"automationId":{"type":"string"}},"required":["runId","automationId"],"additionalProperties":false},"MealImageDescriptionDto":{"type":"object","properties":{"text":{"type":"string"}},"required":["text"],"additionalProperties":false},"MediaStatusResponseDto":{"type":"object","properties":{"mediaId":{"type":"string"},"processingStatus":{"type":"string"},"outputs":{"anyOf":[{"$ref":"#/components/schemas/Record<string,string>"},{"type":"null"}]},"urls":{"anyOf":[{"$ref":"#/components/schemas/Record<string,string>"},{"type":"null"}]},"error":{"type":["string","null"]}},"required":["mediaId","processingStatus"],"additionalProperties":false},"MediaUploadUrlRequestDto":{"type":"object","properties":{"filename":{"type":"string"},"contentType":{"type":"string"},"categoryId":{"type":["string","null"]},"usageType":{"anyOf":[{"$ref":"#/components/schemas/MediaUsageType"},{"type":"null"}]},"userId":{"type":["string","null"]},"preCompressed":{"type":["boolean","null"]}},"required":["filename","contentType"],"additionalProperties":false},"MediaUploadUrlResponseDto":{"type":"object","properties":{"uploadUrl":{"type":"string"},"mediaId":{"type":"string"},"expiresAt":{"type":"string"}},"required":["uploadUrl","mediaId","expiresAt"],"additionalProperties":false},"MedicalConditionDto":{"type":"object","properties":{"id":{"type":["string","null"]},"name":{"type":"string"},"diagnosedDate":{"type":["string","null"]},"notes":{"type":["string","null"]},"medications":{"type":"array","items":{"type":"string"}},"riskLevel":{"$ref":"#/components/schemas/HealthRiskLevel"}},"required":["id","name","diagnosedDate","notes","medications","riskLevel"],"additionalProperties":false,"description":"No"},"HealthRiskLevel":{"type":"string","enum":["LOW","MODERATE","HIGH","EXTREME"]},"MeetingTranscriptRequestDto":{"type":"object","properties":{"appointmentId":{"type":["string","null"]},"participantInfos":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ParticipantInfoDto"}},{"type":"null"}]},"transcript":{"type":["string","null"]},"summary":{"type":["string","null"]}},"additionalProperties":false,"description":"Accepted request body for create/update. Mirrors the same MeetingTranscriptDto; only appointmentId, participantInfos, transcript, summary are read by the service."},"MeetingTranscriptResponseDto":{"type":"object","properties":{"id":{"type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"tenantId":{"type":["string","null"]},"appointmentId":{"type":["string","null"]},"appointment":{"anyOf":[{},{"type":"null"}]},"participantInfos":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ParticipantInfoDto"}},{"type":"null"}]},"transcript":{"type":["string","null"]},"summary":{"type":["string","null"]}},"required":["id","createdAt","updatedAt","tenantId","appointmentId","appointment","participantInfos","transcript","summary"],"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.MeetingTranscriptDto.\n\nIMPORTANT: this DTO has NO"},"MessageDto":{"type":"object","properties":{"id":{"type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"content":{"type":["string","null"]},"type":{"anyOf":[{"$ref":"#/components/schemas/MessageType"},{"type":"null"}]},"source":{"anyOf":[{"$ref":"#/components/schemas/MessageSource"},{"type":"null"}]},"senderId":{"type":["string","null"]},"senderName":{"type":["string","null"]},"senderAvatarUrl":{"type":["string","null"]},"conversationId":{"type":["string","null"]},"sentAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"participantStatuses":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ParticipantStatus"}},{"type":"null"}]},"media":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/MediaDto"}},{"type":"null"}]},"parentMessageId":{"type":["string","null"]},"metadata":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]},"entities":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/MessageEntity"}},{"type":"null"}]},"contentFormat":{"type":["number","null"]},"editedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"additionalProperties":false,"description":"Ports com.protocolcrm.api.dto.conversation.{MessageDto, ForwardMessageRequestDto}.\n\nCONTRACT: MessageDto has NO"},"ParticipantStatus":{"type":"object","properties":{"participantId":{"type":"string"},"status":{"$ref":"#/components/schemas/MessageStatus"},"timestamp":{"type":"string","format":"date-time"}},"required":["participantId","status","timestamp"],"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.model.communication.ParticipantStatus (a plain data class persisted inside the participant_statuses jsonb array)."},"NotificationDto":{"type":"object","properties":{"id":{"type":["string","null"]},"type":{"type":"string"},"title":{"type":"string"},"message":{"type":"string"},"recipientId":{"type":"string"},"isRead":{"type":"boolean"},"readAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"priority":{"type":"string"},"metadata":{"$ref":"#/components/schemas/Record<string,unknown>"},"relatedEntityId":{"type":["string","null"]},"relatedEntityType":{"type":["string","null"]},"actorId":{"type":["string","null"]},"actorName":{"type":["string","null"]},"actionUrl":{"type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["type","title","message","recipientId","isRead","priority","metadata"],"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.notification.NotificationDto (@JsonInclude(JsonInclude.Include.NON_NULL)).\n\nSerialization order follows the Kotlin data-class primary constructor:   id, type, title, message, recipientId, isRead, readAt, priority, metadata,   relatedEntityId, relatedEntityType, actorId, actorName, actionUrl,   createdAt, updatedAt (id/createdAt/updatedAt are `override val` constructor params from BaseTenantDto, so they keep their position in this data class's own constructor — NOT hoisted to the top.)\n\nNON_NULL → null/undefined keys are stripped on the way out (see mapToDto). Note that `isRead` (Boolean, non-null) and `priority`/`type` (non-null enums) and `metadata` (defaults to empty map) are always present. `metadata` is emitted even when `{}` because an empty map is non-null under NON_NULL."},"NotificationListParams":{"type":"object","properties":{"page":{"type":"number"},"pageSize":{"type":"number"},"sortBy":{"type":"string"},"sortDir":{"type":"string","enum":["ASC","DESC"]},"types":{"type":"array","items":{"type":"string"},"description":"CONTRACT EXTENSION (ECS-1102, beyond Kotlin parity): `type` accepts a CSV or repeated query param (?type=A,B / ?type=A&type=B) and filters with IN. A single value behaves exactly as the original single-valued filter."},"isRead":{"type":"boolean"},"priority":{"type":"string"},"startDate":{"type":"string","format":"date-time"},"endDate":{"type":"string","format":"date-time"},"actorId":{"type":"string"},"searchTerm":{"type":"string"}},"required":["page","pageSize","sortBy","sortDir"],"additionalProperties":false,"description":"Filter params for the list / mark-all-read endpoints. Mirrors com.protocolcrm.api.dto.notification.NotificationFilters plus the list endpoint's pagination/sort"},"NutritionTemplateDto":{"type":"object","properties":{"id":{"type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"name":{"type":["string","null"]},"description":{"type":["string","null"]},"items":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/NutritionTemplateItem"}},{"type":"null"}]},"nutrients":{"anyOf":[{"$ref":"#/components/schemas/NutrientsInfo"},{"type":"null"}]},"image":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"noImage":{"type":["boolean","null"]},"isTemplate":{"type":["boolean","null"]},"attachments":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/MediaDto"}},{"type":"null"}]},"translations":{"anyOf":[{"$ref":"#/components/schemas/Record<string,NutritionTemplateTranslations>"},{"type":"null"}]},"userId":{"type":["string","null"]},"ownerId":{"type":["string","null"]},"visibility":{"anyOf":[{"$ref":"#/components/schemas/ResourceVisibility"},{"type":"null"}],"description":"Explicit PRIVATE/TEAM share marker (TEAM-mode tenants). On output: always emitted (`template.visibility ?? null`). On input (the DTO doubles as the request body via Partial): only applied when explicitly present; sharing changes are owner/admin- gated in TEAM mode."},"tags":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"categoryId":{"type":["string","null"]},"externalId":{"type":["string","null"]},"externalUpdatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"templateMode":{"anyOf":[{"$ref":"#/components/schemas/NutritionTemplateMode"},{"type":"null"}]},"metadata":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]}},"required":["id","createdAt","updatedAt","name","description","items","nutrients","image","noImage","isTemplate","attachments","translations","userId","ownerId","visibility","tags","categoryId","externalId","externalUpdatedAt","templateMode","metadata"],"additionalProperties":false,"description":"Ports com.protocolcrm.api.dto.nutrition.NutritionTemplateDto.\n\nCONTRACT: NutritionTemplateDto has NO"},"NutritionTemplateItem":{"type":"object","properties":{"id":{"type":["string","null"]},"foodId":{"type":["string","null"]},"name":{"type":["string","null"]},"foodImage":{"type":["string","null"]},"quantity":{"type":["number","null"]},"measureName":{"type":["string","null"]},"measureLabel":{"type":["string","null"]},"measureUri":{"type":["string","null"]},"allMeasures":{"anyOf":[{"type":"array","items":{}},{"type":"null"}]},"nutrients":{"anyOf":[{"$ref":"#/components/schemas/NutrientsInfo"},{"type":"null"}]},"isOptional":{"type":["boolean","null"]},"notes":{"type":["string","null"]},"supplementId":{"type":["number","null"]},"dosage":{"type":["string","null"]},"timing":{"type":["string","null"]},"frequency":{"type":["string","null"]},"items":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/NutritionTemplateItem"}},{"type":"null"}]},"expectedCalories":{"type":["number","null"]},"expectedMeasureWeight":{"type":["number","null"]},"quantityMin":{"type":["number","null"]},"quantityMax":{"type":["number","null"]},"type":{"$ref":"#/components/schemas/NutritionTemplateType"},"translations":{"anyOf":[{"$ref":"#/components/schemas/Record<string,NutritionItemTranslations>"},{"type":"null"}]},"nutrientsPerGram":{"anyOf":[{"$ref":"#/components/schemas/NutrientsInfo"},{"type":"null"}]},"templateId":{"type":["string","null"]},"embeddedTemplate":{"anyOf":[{},{"type":"null"}]},"expandedTemplate":{"anyOf":[{},{"type":"null"}]}},"required":["type"],"additionalProperties":false},"NutritionTemplateType":{"type":"string","enum":["MEAL","SUPPLEMENT","GROUP","LABEL","TEMPLATE","EMBEDDED_TEMPLATE"]},"Record<string,NutritionItemTranslations>":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/NutritionItemTranslations"}},"NutritionItemTranslations":{"type":"object","properties":{"name":{"type":["string","null"]},"notes":{"type":["string","null"]}},"additionalProperties":false},"Record<string,NutritionTemplateTranslations>":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/NutritionTemplateTranslations"}},"NutritionTemplateTranslations":{"type":"object","properties":{"name":{"type":["string","null"]},"description":{"type":["string","null"]}},"additionalProperties":false},"NutritionTemplateMode":{"type":"string","enum":["SIMPLE","ADVANCED"],"description":"Shared nutrition JSONB sub-objects and enums.\n\nThese mirror the Kotlin model types stored verbatim in JSONB columns (nutrition_template.items/image/attachments/nutrients/translations/metadata and nutrition_logs.items/image/total_nutrients). They flow through read/write unchanged; the load-bearing thing is the field NAMES and the"},"OnboardingDataDto":{"type":"object","properties":{"businessName":{"type":["string","null"]},"tagline":{"type":["string","null"]},"yearsExperience":{"type":["string","null"]},"certifications":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"specializations":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"trainingStyle":{"type":["string","null"]},"targetDemographics":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"profilePhoto":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"logo":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"primaryColor":{"type":["string","null"]},"secondaryColor":{"type":["string","null"]},"primaryGoal":{"type":["string","null"]},"monthlyClientTarget":{"type":["number","null"]},"referralSource":{"type":["string","null"]},"country":{"type":["string","null"]},"timezone":{"type":["string","null"]}},"additionalProperties":false,"description":"Mirrors OnboardingDataDto. Declaration order preserved to match the Kotlin DTO (relevant for parity, though NON_NULL stripping removes absent keys anyway)."},"OnboardingRequestDto":{"type":"object","properties":{"healthProfile":{"anyOf":[{"$ref":"#/components/schemas/UserHealthProfileRequestDto"},{"type":"null"}]},"fitnessProfile":{"anyOf":[{"$ref":"#/components/schemas/UserFitnessProfileDto"},{"type":"null"}]},"behavioralProfile":{"anyOf":[{"$ref":"#/components/schemas/UserBehavioralProfileRequestDto"},{"type":"null"}]},"biometricProfile":{"anyOf":[{},{"type":"null"}]},"genomicProfile":{"anyOf":[{"$ref":"#/components/schemas/GenomicProfileRequestDto"},{"type":"null"}]},"nutritionProfile":{"anyOf":[{"$ref":"#/components/schemas/UserNutritionProfileDto"},{"type":"null"}]}},"additionalProperties":false},"UserHealthProfileRequestDto":{"type":"object","properties":{"dateOfBirth":{"type":["string","null"]},"gender":{"anyOf":[{"$ref":"#/components/schemas/Gender"},{"type":"null"}]},"heightCm":{"type":["number","null"]},"notes":{"type":["string","null"]},"allergies":{"type":"array","items":{"type":"string"}},"clientDeniesAllergies":{"type":["boolean","null"]},"previousInjuries":{"type":"array","items":{"type":"string"}},"clientDeniesPreviousInjuries":{"type":["boolean","null"]},"lifestyleFactors":{"$ref":"#/components/schemas/Record<string,unknown>"},"riskAssessment":{"$ref":"#/components/schemas/HealthRiskLevel"},"medicalConditions":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/MedicalConditionDto"}},{"type":"null"}]},"clientDeniesMedicalConditions":{"type":["boolean","null"]},"assessmentResults":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/PhysicalAssessmentResultDto"}},{"type":"null"}]}},"additionalProperties":false},"PhysicalAssessmentResultDto":{"type":"object","properties":{"id":{"type":["string","null"]},"assessmentType":{"$ref":"#/components/schemas/PhysicalAssessmentType"},"date":{"type":"string"},"score":{"type":"number"},"unit":{"type":["string","null"]},"notes":{"type":["string","null"]},"percentile":{"type":["number","null"]}},"required":["id","assessmentType","date","score","unit","notes","percentile"],"additionalProperties":false,"description":"No"},"PhysicalAssessmentType":{"type":"string","enum":["BODY_COMPOSITION","STRENGTH","ENDURANCE","FLEXIBILITY","MOBILITY","BALANCE","SPEED","POWER"]},"UserFitnessProfileDto":{"type":"object","properties":{"experienceLevel":{"anyOf":[{"$ref":"#/components/schemas/ExperienceLevel"},{"type":"null"}]},"primaryGoals":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/FitnessGoal"}},{"type":"null"}]},"preferredTrainingStyles":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/TrainingStyle"}},{"type":"null"}]},"availableDays":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}]},"maxSessionsPerWeek":{"type":["number","null"]},"sessionDurationPreferenceMinutes":{"type":["number","null"]},"equipmentAccessLevel":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/Equipment"}},{"type":"null"}]},"notes":{"type":["string","null"]}},"additionalProperties":false},"FitnessGoal":{"type":"string"},"TrainingStyle":{"type":"string"},"UserBehavioralProfileRequestDto":{"type":"object","properties":{"userId":{"type":["string","null"]},"notes":{"type":["string","null"]},"motivationFactors":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/MotivationFactor"}},{"type":"null"}]},"adherenceRate":{"type":["number","null"]},"consistencyScore":{"type":["number","null"]},"preferredCommunicationStyles":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/CommunicationStyle"}},{"type":"null"}]},"preferredCommunicationFrequency":{"anyOf":[{"$ref":"#/components/schemas/CommunicationFrequency"},{"type":"null"}]},"preferredCommunicationChannels":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/CommunicationChannel"}},{"type":"null"}]},"preferredFeedbackStyles":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/FeedbackStyle"}},{"type":"null"}]},"responsesToChallenges":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ChallengeResponse"}},{"type":"null"}]},"behavioralTriggers":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]}},"additionalProperties":false},"MotivationFactor":{"type":"string"},"CommunicationStyle":{"type":"string"},"CommunicationFrequency":{"type":"string","enum":["DAILY","OFTEN","WEEKLY","BI_WEEKLY","MONTHLY"]},"CommunicationChannel":{"type":"string"},"FeedbackStyle":{"type":"string"},"ChallengeResponse":{"type":"string"},"UserNutritionProfileDto":{"type":"object","properties":{"primaryDietType":{"anyOf":[{"$ref":"#/components/schemas/DietType"},{"type":"null"}]},"nutritionGoals":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/NutritionGoal"}},{"type":"null"}]},"preferredMealFrequency":{"anyOf":[{"$ref":"#/components/schemas/MealFrequency"},{"type":"null"}]},"foodPreferences":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/FoodPreferenceDto"}},{"type":"null"}]},"cuisinePreferences":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/CuisineType"}},{"type":"null"}]},"dietaryRestrictions":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/DietaryRestrictionDto"}},{"type":"null"}]},"previousDietSuccess":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"dietChallenges":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/DietChallenge"}},{"type":"null"}]},"nutritionRelatedHealthConcerns":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/HealthConcern"}},{"type":"null"}]},"notes":{"type":["string","null"]},"lastUpdatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"additionalProperties":false},"DietType":{"type":"string"},"NutritionGoal":{"type":"string"},"MealFrequency":{"type":"string"},"FoodPreferenceDto":{"type":"object","properties":{"foodCategory":{"type":"string"},"preferenceLevel":{"type":"string"},"specificFoods":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"notes":{"type":["string","null"]}},"required":["foodCategory","preferenceLevel"],"additionalProperties":false,"description":"Stored inside the nutrition profile's JSONB columns (FoodPreferenceDto / DietaryRestrictionDto)."},"CuisineType":{"type":"string"},"DietaryRestrictionDto":{"type":"object","properties":{"food":{"type":"string"},"isAllergy":{"type":"boolean"},"severity":{"type":"number"},"alternatives":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]}},"required":["food","isAllergy","severity"],"additionalProperties":false},"DietChallenge":{"type":"string"},"HealthConcern":{"type":"string"},"OnboardingStatusDto":{"type":"object","properties":{"status":{"type":"string"},"completedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"skippedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["status","completedAt","skippedAt"],"additionalProperties":false,"description":"Mirrors OnboardingStatusDto (no"},"PasswordResetDto":{"type":"object","properties":{"code":{"type":"string"},"email":{"type":"string"},"newPassword":{"type":"string"},"tenantId":{"type":"string"}},"required":["code","email","newPassword"],"additionalProperties":false,"description":"Mirrors PasswordResetDto."},"PasswordResetRequestDto":{"type":"object","properties":{"email":{"type":"string"},"tenantId":{"type":"string"}},"required":["email"],"additionalProperties":false,"description":"Mirrors PasswordResetRequestDto."},"PausePurchaseRequest":{"type":"object","properties":{"resumeAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.shop.PausePurchaseRequest."},"PreparedNutritionLogDto":{"type":"object","properties":{"inputType":{"$ref":"#/components/schemas/NutritionLogInputType"},"originalInput":{"type":["string","null"]},"image":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"items":{"type":"array","items":{"$ref":"#/components/schemas/NutritionLogItem"}},"totalNutrients":{"$ref":"#/components/schemas/NutrientsInfo"}},"required":["inputType","originalInput","image","items","totalNutrients"],"additionalProperties":false},"ProductType":{"type":"string","enum":["SERVICE","SESSION_PACK","BUNDLE","DIGITAL","PHYSICAL"]},"FulfillmentMethod":{"type":"string","enum":["STRIPE_CHECKOUT","MOBILE_IAP","EXTERNAL_PAYMENT","FREE"]},"ProductMetadata":{"type":"object","properties":{"externalPlatformUrl":{"type":["string","null"]},"fulfillmentInstructions":{"type":["string","null"]},"richDescription":{"type":["string","null"]},"media":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/Record<string,unknown>"}},{"type":"null"}]},"isFeatured":{"type":["boolean","null"]},"sortOrder":{"type":["number","null"]},"tags":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"customFields":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.model.shop.Product (table: products, extends BaseTenantEntity).\n\nEnum columns (pricing_type, billing_interval, product_type, fulfillment_method) are all"},"ProgramDto":{"type":"object","properties":{"id":{"type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"name":{"type":["string","null"]},"description":{"type":["string","null"]},"userId":{"type":["string","null"]},"createdById":{"type":["string","null"]},"phases":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ProgramPhase"}},{"type":"null"}]},"startDate":{"type":["string","null"]},"endDate":{"type":["string","null"]},"pausedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"resumeAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"stagedRollout":{"type":["boolean","null"]},"rolloutMaxLookupWeeks":{"type":["number","null"]},"rolloutIntervalWeeks":{"type":["number","null"]},"isTemplate":{"type":["boolean","null"]},"isPublicLibrary":{"type":["boolean","null"]},"isActive":{"type":["boolean","null"]},"status":{"anyOf":[{"$ref":"#/components/schemas/ProgramStatus"},{"type":"null"}]},"totalDurationWeeks":{"type":["number","null"]},"programGoal":{"type":["string","null"]},"difficultyLevel":{"type":["string","null"]},"tags":{"type":["string","null"]},"planningType":{"anyOf":[{"$ref":"#/components/schemas/PlanningType"},{"type":"null"}]},"programType":{"anyOf":[{"$ref":"#/components/schemas/ProgramType"},{"type":"null"}]},"sections":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ProgramSection"}},{"type":"null"}]},"nutritionPlanningType":{"anyOf":[{"$ref":"#/components/schemas/NutritionPlanningType"},{"type":"null"}]},"workoutPlanningOptions":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/WorkoutPlanningOption"}},{"type":"null"}]},"contentCollections":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ProgramContentCollection"}},{"type":"null"}]},"image":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"video":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"daysPreviewType":{"anyOf":[{"$ref":"#/components/schemas/DaysPreviewType"},{"type":"null"}]},"externalId":{"type":["string","null"]},"externalUpdatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"ownerId":{"type":["string","null"]},"visibility":{"anyOf":[{"$ref":"#/components/schemas/ResourceVisibility"},{"type":"null"}],"description":"Explicit PRIVATE/TEAM share marker (TEAM-mode tenants)."},"categoryId":{"type":["string","null"]},"templateId":{"type":["string","null"]},"duplicateContentProgramId":{"type":["string","null"]},"placeholderValues":{"anyOf":[{"$ref":"#/components/schemas/Record<string,string>"},{"type":"null"}],"description":"Per-client `{{key}}` -> value map; content keeps the tokens, resolved at render time."},"purchaseId":{"type":["string","null"]},"purchase":{"anyOf":[{"$ref":"#/components/schemas/PurchaseSubsetDto"},{"type":"null"}]},"externalPurchaseId":{"type":["string","null"]},"position":{"type":["number","null"]},"currentWeek":{"type":["number","null"]},"totalWeeks":{"type":["number","null"]},"daysUntilEnd":{"type":["number","null"]}},"required":["id","createdAt","updatedAt","name","description","userId","createdById","phases","startDate","endDate","pausedAt","resumeAt","stagedRollout","rolloutMaxLookupWeeks","rolloutIntervalWeeks","isTemplate","isPublicLibrary","isActive","status","totalDurationWeeks","programGoal","difficultyLevel","tags","planningType","programType","sections","nutritionPlanningType","workoutPlanningOptions","contentCollections","image","video","daysPreviewType","externalId","externalUpdatedAt","ownerId","visibility","categoryId","templateId","duplicateContentProgramId","placeholderValues","purchaseId","purchase","externalPurchaseId","position","currentWeek","totalWeeks","daysUntilEnd"],"additionalProperties":false,"description":"Ports com.protocolcrm.api.dto.program.ProgramDto.\n\nCONTRACT: ProgramDto has NO"},"ProgramPhase":{"type":"object","properties":{"id":{"type":["string","null"]},"createdAt":{"type":["string","null"]},"updatedAt":{"type":["string","null"]},"tenantId":{"type":["string","null"]},"name":{"type":["string","null"]},"phaseType":{"anyOf":[{"$ref":"#/components/schemas/PhaseType"},{"type":"null"}]},"nutritionGoal":{"anyOf":[{"$ref":"#/components/schemas/PhaseNutritionGoal"},{"type":"null"}]},"targetNutrientsTrainingDay":{"anyOf":[{"$ref":"#/components/schemas/NutrientsInfo"},{"type":"null"}]},"targetNutrientsRestDay":{"anyOf":[{"$ref":"#/components/schemas/NutrientsInfo"},{"type":"null"}]},"targetNutrientsAverage":{"anyOf":[{"$ref":"#/components/schemas/NutrientsInfo"},{"type":"null"}]},"stepsTarget":{"type":["number","null"]},"repeatsTimes":{"type":["number","null"]},"description":{"type":["string","null"]},"trainingPlanningInstructions":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"nutritionPlanningInstructions":{"type":["string","null"]},"startDate":{"type":["string","null"]},"endDate":{"type":["string","null"]},"workoutDays":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ProgramPhaseDay"}},{"type":"null"}]},"nutritionDays":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ProgramPhaseDay"}},{"type":"null"}]},"activityDays":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ProgramPhaseDay"}},{"type":"null"}]},"supplementationDays":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ProgramPhaseDay"}},{"type":"null"}]},"contentDays":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ProgramPhaseDay"}},{"type":"null"}]},"isActive":{"type":"boolean"},"status":{"anyOf":[{"$ref":"#/components/schemas/ProgramPhaseStatus"},{"type":"null"}]},"statusReport":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/StatusReportItem"}},{"type":"null"}]},"reusePreviousPhaseTrainings":{"type":["boolean","null"]},"reusePreviousPhaseNutrition":{"type":["boolean","null"]},"cardioGeneral":{"type":["string","null"]}},"additionalProperties":{},"description":"ProgramPhase — a plain (non-@JsonInclude) class extending BaseTenantEntity. When serialized inside Program.phases JSONB it carries whatever was stored. We treat it as a JSONB blob that flows through unchanged on read/write; the only structured access we need is for the in-program search (phases[].workoutDays[].workoutIds etc.) and the phase-duplicate deep copy. id/createdAt/updatedAt/tenantId come from the BaseTenantEntity superclass and are present in stored JSON."},"PhaseType":{"type":"string","enum":["FOUNDATION","BUILD","PEAK","DELOAD","TAPER","RECOVERY","MAINTENANCE","SPECIALIZATION","TRANSITION","FAT_LOSS"]},"PhaseNutritionGoal":{"type":"string","enum":["AGGRESSIVE_DEFICIT","DEFICIT","MINOR_DEFICIT","MAINTENANCE","MINOR_SURPLUS","SURPLUS"]},"ProgramPhaseDay":{"type":"object","properties":{"name":{"type":["string","null"]},"workoutIds":{"type":"array","items":{"type":"string"}},"workoutsExpanded":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/WorkoutDto"}},{"type":"null"}]},"plannedNutritionTemplates":{"type":"array","items":{"type":"string"}},"plannedNutritionTemplatesExpanded":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/NutritionTemplateDto"}},{"type":"null"}]},"recoveryActivities":{"type":"array","items":{"$ref":"#/components/schemas/ProgramActivity"}},"mediaIds":{"type":"array","items":{"type":"string"}},"mediaExpanded":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/MediaDto"}},{"type":"null"}]},"notes":{"type":["string","null"]},"isRestDay":{"type":"boolean"},"dayType":{"anyOf":[{"$ref":"#/components/schemas/ProgramPhaseDayType"},{"type":"null"}]},"dayFocus":{"type":["string","null"]}},"required":["workoutIds","plannedNutritionTemplates","recoveryActivities","mediaIds","isRestDay"],"additionalProperties":false,"description":"ProgramPhaseDay —"},"WorkoutDto":{"type":"object","properties":{"id":{"type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"name":{"type":["string","null"]},"description":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"image":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"noImage":{"type":["boolean","null"]},"durationMinutes":{"type":["number","null"]},"difficulty":{"anyOf":[{"$ref":"#/components/schemas/WorkoutDifficulty"},{"type":"null"}]},"unitSystem":{"anyOf":[{"$ref":"#/components/schemas/UnitSystem"},{"type":"null"}]},"experienceLevel":{"anyOf":[{"$ref":"#/components/schemas/ExperienceLevel"},{"type":"null"}]},"energyRequirement":{"type":["number","null"]},"goal":{"anyOf":[{"$ref":"#/components/schemas/TrainingGoal"},{"type":"null"}]},"caloriesBurnedEstimate":{"type":["number","null"]},"isTemplate":{"type":["boolean","null"]},"isPublicLibrary":{"type":["boolean","null"]},"frequencyPerWeek":{"type":["number","null"]},"userId":{"type":["string","null"]},"assignedBy":{"type":["string","null"]},"scheduledDate":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"muscleGroups":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/MuscleGroup"}},{"type":"null"}]},"equipmentRequired":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/Equipment"}},{"type":"null"}]},"tags":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"exerciseGroups":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ExerciseGroup"}},{"type":"null"}]},"timeCapMinutes":{"type":["number","null"]},"targetRounds":{"type":["number","null"]},"restBetweenRoundsSeconds":{"type":["number","null"]},"scalingOptions":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ScalingOption"}},{"type":"null"}]},"runningDistanceMeters":{"type":["number","null"]},"totalStations":{"type":["number","null"]},"tenantId":{"type":["string","null"]},"translations":{"anyOf":[{"$ref":"#/components/schemas/Record<string,WorkoutTranslations>"},{"type":"null"}]},"ownerId":{"type":["string","null"]},"visibility":{"anyOf":[{"$ref":"#/components/schemas/ResourceVisibility"},{"type":"null"}],"description":"Explicit PRIVATE/TEAM share marker (TEAM-mode tenants). Always populated on responses; on the request body it is optional and only applied when explicitly present (sharing changes are owner/admin-gated in TEAM mode). WorkoutDto is the single request+response shape here, so this is `?`-optional unlike programs."},"categoryId":{"type":["string","null"]},"externalId":{"type":["string","null"]},"externalUpdatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"templateMode":{"anyOf":[{"$ref":"#/components/schemas/WorkoutTemplateMode"},{"type":"null"}]},"metadata":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]}},"required":["id","createdAt","updatedAt","name","description","image","noImage","durationMinutes","difficulty","unitSystem","experienceLevel","energyRequirement","goal","caloriesBurnedEstimate","isTemplate","isPublicLibrary","frequencyPerWeek","userId","assignedBy","scheduledDate","muscleGroups","equipmentRequired","tags","exerciseGroups","timeCapMinutes","targetRounds","restBetweenRoundsSeconds","scalingOptions","runningDistanceMeters","totalStations","tenantId","translations","ownerId","categoryId","externalId","externalUpdatedAt","templateMode","metadata"],"additionalProperties":false,"description":"Ports com.protocolcrm.api.dto.workout.* response DTOs.\n\nCONTRACT per-DTO"},"WorkoutDifficulty":{"type":"string","enum":["EASY","MODERATE","HARD","VERY_HARD"]},"UnitSystem":{"type":"string","enum":["METRIC","IMPERIAL"]},"TrainingGoal":{"type":"string","enum":["WEIGHT_LOSS","MUSCLE_GAIN","STRENGTH","ENDURANCE","FLEXIBILITY","SPORT_SPECIFIC","GENERAL_FITNESS","REHABILITATION"]},"ExerciseGroup":{"type":"object","properties":{"id":{"type":["string","null"]},"name":{"type":["string","null"]},"format":{"$ref":"#/components/schemas/ExerciseGroupFormat"},"exercises":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ExerciseWithSets"}},{"type":"null"}]},"rounds":{"type":["number","null"]},"restAfterGroupSeconds":{"type":["number","null"]},"restBetweenRoundsSeconds":{"type":["number","null"]},"timeCapSeconds":{"type":["number","null"]},"notes":{"type":["string","null"]},"section":{"anyOf":[{"$ref":"#/components/schemas/WorkoutSection"},{"type":"null"}]},"translations":{"anyOf":[{"$ref":"#/components/schemas/Record<string,ExerciseGroupTranslations>"},{"type":"null"}]}},"additionalProperties":false},"ExerciseWithSets":{"type":"object","properties":{"id":{"type":["string","null"]},"exerciseId":{"type":["string","null"]},"name":{"type":["string","null"]},"image":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"video":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"sets":{"type":["number","null"]},"repRule":{"type":["string","null"]},"restAfterSeconds":{"type":["number","null"]},"rpe":{"type":["string","null"]},"rir":{"type":["string","null"]},"tempo":{"type":["string","null"]},"notes":{"type":["string","null"]},"weight":{"type":["string","null"]},"section":{"anyOf":[{"$ref":"#/components/schemas/WorkoutSection"},{"type":"null"}]},"previewLoops":{"type":["boolean","null"]},"translations":{"anyOf":[{"$ref":"#/components/schemas/Record<string,ExerciseWithSetsTranslations>"},{"type":"null"}]}},"additionalProperties":false},"Record<string,ExerciseWithSetsTranslations>":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ExerciseWithSetsTranslations"}},"ExerciseWithSetsTranslations":{"type":"object","additionalProperties":{}},"Record<string,ExerciseGroupTranslations>":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ExerciseGroupTranslations"}},"ExerciseGroupTranslations":{"type":"object","additionalProperties":{}},"ScalingOption":{"type":"object","properties":{"level":{"type":"string"},"description":{"type":"string"},"exerciseSubstitutions":{"anyOf":[{"$ref":"#/components/schemas/Record<string,string>"},{"type":"null"}]}},"required":["level","description"],"additionalProperties":false},"Record<string,WorkoutTranslations>":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/WorkoutTranslations"}},"WorkoutTranslations":{"type":"object","additionalProperties":{}},"WorkoutTemplateMode":{"type":"string","enum":["SIMPLE","ADVANCED","VIDEO_GUIDED"],"description":"WorkoutTemplateMode is stored as a SMALLINT ORDINAL in Postgres — the Kotlin field has NO"},"ProgramActivity":{"type":"object","properties":{"type":{"anyOf":[{"$ref":"#/components/schemas/RecoveryActivityType"},{"type":"null"}]},"durationMinutes":{"type":["number","null"]},"timeOfDay":{"type":["string","null"]},"instructions":{"type":["string","null"]},"equipmentNeeded":{"type":["string","null"]},"intensity":{"type":["string","null"]},"notes":{"type":["string","null"]}},"additionalProperties":false,"description":"ProgramActivity —"},"RecoveryActivityType":{"type":"string"},"ProgramPhaseDayType":{"type":"string","enum":["REST","WORKOUT","CARDIO"]},"ProgramPhaseStatus":{"type":"string","enum":["DRAFT","EMPTY","READY","DIRTY"]},"StatusReportItem":{"type":"object","properties":{"message":{"type":["string","null"]},"type":{"type":["string","null"]},"action":{"type":["string","null"]},"sector":{"type":["string","null"]}},"additionalProperties":false,"description":"StatusReportItem —"},"ProgramStatus":{"type":"string","enum":["PENDING","ACTIVE","EXPIRED","PAUSED"]},"PlanningType":{"type":"string","enum":["SIMPLE","PHASED","WEEKLY"]},"ProgramType":{"type":"string","enum":["FULL","WORKOUT","NUTRITION"]},"ProgramSection":{"type":"string","enum":["WORKOUT","NUTRITION","OTHER_ACTIVITIES","SUPPLEMENTATION","CONTENT"]},"NutritionPlanningType":{"type":"string","enum":["AVERAGE","ACTIVE_REST"]},"WorkoutPlanningOption":{"type":"string","enum":["WORKOUTS","STEPS","CARDIO_GENERAL"]},"ProgramContentCollection":{"type":"object","properties":{"id":{"type":["string","null"]},"name":{"type":["string","null"]},"description":{"type":["string","null"]},"type":{"type":["string","null"]},"image":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"items":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ProgramContentItem"}},{"type":"null"}]},"mediaIds":{"type":"array","items":{"type":"string"}},"mediaExpanded":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/MediaDto"}},{"type":"null"}]},"metadata":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]}},"required":["mediaIds"],"additionalProperties":{},"description":"ProgramContentCollection —"},"ProgramContentItem":{"type":"object","properties":{"id":{"type":["string","null"]},"type":{"anyOf":[{"$ref":"#/components/schemas/ProgramContentItemType"},{"type":"null"}]},"media":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"nutritionTemplate":{"anyOf":[{"$ref":"#/components/schemas/NutritionTemplateDto"},{"type":"null"}]},"workout":{"anyOf":[{"$ref":"#/components/schemas/WorkoutDto"},{"type":"null"}]},"subcollection":{"anyOf":[{"$ref":"#/components/schemas/ProgramContentCollection"},{"type":"null"}]},"name":{"type":["string","null"]},"description":{"type":["string","null"]},"sortOrder":{"type":["number","null"]},"metadata":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]}},"additionalProperties":{},"description":"ProgramContentItem —"},"ProgramContentItemType":{"type":"string","enum":["MEDIA","NUTRITION_TEMPLATE","SUBCOLLECTION","WORKOUT"]},"DaysPreviewType":{"type":"string","enum":["DAY_NUMBER","DAY_NAME","DAY_DATE_SHORT","DAY_DATE_FULL"]},"PurchaseSubsetDto":{"type":"object","properties":{"id":{"type":["string","null"]},"productId":{"type":["string","null"]},"name":{"type":["string","null"]},"status":{"type":["string","null"]},"purchasedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"expiresAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"pausedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"resumeAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"externalId":{"type":["string","null"]},"externalSource":{"type":["string","null"]}},"additionalProperties":false,"description":"The exact subset of PurchaseDto fields that mapToProgramDto populates. PurchaseDto is"},"ProgramRequestDto":{"type":"object","properties":{"id":{"type":["string","null"]},"name":{"type":["string","null"]},"description":{"type":["string","null"]},"userId":{"type":["string","null"]},"createdById":{"type":["string","null"]},"phases":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ProgramPhase"}},{"type":"null"}]},"startDate":{"type":["string","null"]},"endDate":{"type":["string","null"]},"pausedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"resumeAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"stagedRollout":{"type":["boolean","null"]},"rolloutMaxLookupWeeks":{"type":["number","null"]},"rolloutIntervalWeeks":{"type":["number","null"]},"isTemplate":{"type":["boolean","null"]},"isPublicLibrary":{"type":["boolean","null"]},"isActive":{"type":["boolean","null"]},"status":{"anyOf":[{"$ref":"#/components/schemas/ProgramStatus"},{"type":"null"}]},"totalDurationWeeks":{"type":["number","null"]},"programGoal":{"type":["string","null"]},"difficultyLevel":{"type":["string","null"]},"tags":{"type":["string","null"]},"planningType":{"anyOf":[{"$ref":"#/components/schemas/PlanningType"},{"type":"null"}]},"programType":{"anyOf":[{"$ref":"#/components/schemas/ProgramType"},{"type":"null"}]},"sections":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ProgramSection"}},{"type":"null"}]},"nutritionPlanningType":{"anyOf":[{"$ref":"#/components/schemas/NutritionPlanningType"},{"type":"null"}]},"workoutPlanningOptions":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/WorkoutPlanningOption"}},{"type":"null"}]},"contentCollections":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ProgramContentCollection"}},{"type":"null"}]},"image":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"video":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"daysPreviewType":{"anyOf":[{"$ref":"#/components/schemas/DaysPreviewType"},{"type":"null"}]},"externalId":{"type":["string","null"]},"externalUpdatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"ownerId":{"type":["string","null"]},"visibility":{"anyOf":[{"$ref":"#/components/schemas/ResourceVisibility"},{"type":"null"}],"description":"Only applied when explicitly present; sharing changes are owner/admin-gated in TEAM mode."},"categoryId":{"type":["string","null"]},"templateId":{"type":["string","null"]},"duplicateContentProgramId":{"type":["string","null"]},"placeholderValues":{"anyOf":[{"$ref":"#/components/schemas/Record<string,string>"},{"type":"null"}],"description":"Assignment-time `{{key}}` values; replaced into the deep-copied nutrition content."},"purchaseId":{"type":["string","null"]},"externalPurchaseId":{"type":["string","null"]},"position":{"type":["number","null"]}},"additionalProperties":false,"description":"Request shape accepted by create/update (the inbound ProgramDto)."},"ProgressEntryDto":{"type":"object","properties":{"id":{"type":"string"},"trainerId":{"type":"string"},"userId":{"type":"string"},"entryDate":{"type":"string"},"trainerNotes":{"type":"string"},"userNotes":{"type":"string"},"internalNotes":{"type":["string","null"],"description":"Input: explicit null clears the note; absent key means \"no change\" (ECS-1095)."},"media":{"type":"array","items":{"$ref":"#/components/schemas/MediaDto"}},"measurements":{"$ref":"#/components/schemas/ProgressMeasurement"},"answers":{"type":"array","items":{"$ref":"#/components/schemas/ProgressFormAnswerDto"}},"formName":{"type":"string"},"formId":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"status":{"type":"string"},"assigneeIds":{"type":"array","items":{"type":"string"}},"labels":{"type":"array","items":{"$ref":"#/components/schemas/ProgressLabelDto"}},"source":{"$ref":"#/components/schemas/ProgressEntrySourceValue","description":"Output only (ECS-943): server-inferred creation source; ignored on input."}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.user.ProgressEntryDto (@JsonInclude(NON_NULL)). Field order matches the Kotlin data class. Null/undefined keys are stripped from the response (see toProgressEntryDto). On INPUT, all fields are optional request body keys."},"ProgressMeasurement":{"type":"object","properties":{"weightKg":{"type":["number","null"]},"heightCm":{"type":["number","null"]},"bmi":{"type":["number","null"]},"bodyFatPercentage":{"type":["number","null"]},"bodyFatWeightKg":{"type":["number","null"]},"muscleMassPercentage":{"type":["number","null"]},"muscleMassWeightKg":{"type":["number","null"]},"leanMassPercentage":{"type":["number","null"]},"leanMassWeightKg":{"type":["number","null"]},"chestCm":{"type":["number","null"]},"waistCm":{"type":["number","null"]},"waistAtNavelCm":{"type":["number","null"]},"hipsCm":{"type":["number","null"]},"leftArmCm":{"type":["number","null"]},"rightArmCm":{"type":["number","null"]},"leftThighCm":{"type":["number","null"]},"rightThighCm":{"type":["number","null"]},"leftAboveKneeCm":{"type":["number","null"]},"rightAboveKneeCm":{"type":["number","null"]},"leftCalfCm":{"type":["number","null"]},"rightCalfCm":{"type":["number","null"]},"benchPress1rm":{"type":["number","null"]},"benchPress10rm":{"type":["number","null"]},"squat1rm":{"type":["number","null"]},"squat10rm":{"type":["number","null"]},"deadlift1rm":{"type":["number","null"]},"deadlift10rm":{"type":["number","null"]},"pullupsMax":{"type":["number","null"]},"kmTimeSeconds":{"type":["number","null"]},"vo2Max":{"type":["number","null"]},"bloodPressureSystolicMmHg":{"type":["number","null"]},"bloodPressureDiastolicMmHg":{"type":["number","null"]},"sleepAverageHours":{"type":["number","null"]},"sleepQualityScore":{"type":["number","null"]},"energy":{"type":["number","null"]},"mood":{"type":["number","null"]},"adherence":{"type":["number","null"]}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.model.user.ProgressMeasurement (@JsonInclude(NON_NULL)). Stored as a jsonb blob on progress_entry.measurements. All fields optional (Float? in Kotlin). Nulls are stripped on the way out (see stripNullishDeep on the wrapping DTO)."},"ProgressFormAnswerDto":{"type":"object","properties":{"questionTitle":{"type":"string"},"answer":{},"questionId":{"type":"string"},"questionDescription":{"type":["string","null"]},"mapTo":{"type":["string","null"]},"customMapTo":{"type":["string","null"]},"customPurpose":{"type":["string","null"]}},"required":["questionTitle","answer","questionId"],"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.FormAnswerDto (@JsonInclude(NON_NULL)). Stored inside progress_entry.answers jsonb. `answer` is Any (string | string[] | boolean | number)."},"ProgressLabelDto":{"type":"object","properties":{"id":{"type":["string","null"]},"createdAt":{"type":["string","null"]},"updatedAt":{"type":["string","null"]},"tenantId":{"type":["string","null"]},"sector":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"displayOrder":{"type":"number"},"active":{"type":"boolean"}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.user.LabelDto as embedded in progress_entry.labels jsonb. Passed through unchanged for this surface."},"ProgressEntrySourceValue":{"type":"string","enum":["REMINDER","AD_HOC","COACH"],"description":"ECS-943: progress entry creation source. Server-inferred, never client-settable. Mirrors ProgressEntrySource in apps/shared/src/models/progressEntry.ts."},"ProgressEntryFilterParams":{"type":"object","properties":{"page":{"type":"number"},"pageSize":{"type":"number"},"sortBy":{"type":"string"},"sortDir":{"type":"string","enum":["ASC","DESC"]},"searchTerm":{"type":"string"},"userId":{"type":"string"},"trainerId":{"type":"string"},"startDate":{"type":"string"},"endDate":{"type":"string"},"hasMedia":{"type":"boolean"},"hasMeasurements":{"type":"boolean"},"hasTrainerNotes":{"type":"boolean"},"hasUserNotes":{"type":"boolean"},"status":{"type":"array","items":{"type":"string"}},"assigneeId":{"type":"string"},"unassigned":{"type":"boolean"},"allClients":{"type":"boolean"},"source":{"$ref":"#/components/schemas/ProgressEntrySourceValue","description":"ECS-943: AD_HOC also matches legacy rows with NULL source (not-from-reminder)."}},"required":["page","pageSize","sortBy","sortDir"],"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.user.ProgressEntryFilters + the"},"ProgressEntryReportApproveRequest":{"type":"object","properties":{"notifyClient":{"type":"boolean"}},"additionalProperties":false,"description":"Mirrors ProgressEntryReportApproveRequest (notifyClient defaults false)."},"ProgressEntryReportCreateRequest":{"type":"object","properties":{"progressEntryId":{"type":"string"},"clientFacingSummary":{"type":["string","null"]},"internalNotes":{"type":["string","null"]},"priority":{"$ref":"#/components/schemas/ReportPriority"}},"required":["progressEntryId"],"additionalProperties":false,"description":"Mirrors ProgressEntryReportCreateRequest (priority defaults to STANDARD)."},"ProgressEntryReportDto":{"type":"object","properties":{"id":{"type":"string"},"tenantId":{"type":"string"},"progressEntryId":{"type":"string"},"automationId":{"type":"string"},"automationRunId":{"type":"string"},"status":{"$ref":"#/components/schemas/ReportStatus"},"priority":{"$ref":"#/components/schemas/ReportPriority"},"clientFacingSummary":{"type":"string"},"internalNotes":{"type":"string"},"sections":{"$ref":"#/components/schemas/Record<string,unknown>"},"gatheredDataSnapshot":{"$ref":"#/components/schemas/Record<string,unknown>"},"approvedAt":{"type":"string","format":"date-time"},"approvedByUserId":{"type":"string"},"dispatchedToClient":{"type":"boolean"},"clientNotificationId":{"type":"string"},"openedAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["status","priority","sections","gatheredDataSnapshot","dispatchedToClient"],"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.progress.ProgressEntryReportDto (@JsonInclude(NON_NULL)). Field order matches the Kotlin data class. status/priority/sections/gatheredDataSnapshot/ dispatchedToClient are non-nullable and always emitted; nullable fields are stripped when null. (id/tenantId are non-null in practice but typed nullable in Kotlin → stripped if null.)"},"ProgressEntryReportListParams":{"type":"object","properties":{"status":{"$ref":"#/components/schemas/ReportStatus"},"priority":{"$ref":"#/components/schemas/ReportPriority"},"progressEntryId":{"type":"string"},"clientUserId":{"type":"string"},"page":{"type":"number"},"pageSize":{"type":"number"},"sort":{"type":"array","items":{"type":"object","properties":{"property":{"type":"string"},"direction":{"type":"string","enum":["ASC","DESC"]}},"required":["property","direction"],"additionalProperties":false}}},"required":["page","pageSize"],"additionalProperties":false,"description":"The list query params from ProgressEntryReportController + the Spring `Pageable` argument. Spring's default Pageable is 0-indexed (page=0, size=20) — this is the ONE report-domain endpoint that is NOT manually 1-indexed. We faithfully replicate the 0-indexed Pageable semantics: `page` defaults to 0, size to 20."},"ProgressEntryReportRegenerateResponse":{"type":"object","properties":{"runId":{"type":"string"},"automationId":{"type":"string"},"progressEntryId":{"type":"string"}},"required":["runId","automationId","progressEntryId"],"additionalProperties":false,"description":"Mirrors ProgressEntryReportRegenerateResponse."},"ProgressEntryReportUpdateRequest":{"type":"object","properties":{"clientFacingSummary":{"type":["string","null"]},"internalNotes":{"type":["string","null"]},"priority":{"anyOf":[{"$ref":"#/components/schemas/ReportPriority"},{"type":"null"}]},"sections":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]}},"additionalProperties":false,"description":"Mirrors ProgressEntryReportUpdateRequest (all optional)."},"ProgressEntryStatusDto":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"tenantId":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"displayOrder":{"type":"number"},"active":{"type":"boolean"}},"required":["name","color","displayOrder","active"],"additionalProperties":false,"description":"Mirrors ProgressEntryStatusDto (@JsonInclude(NON_NULL)) and ProgressEntryStatusUpsertDto. Field order matches the Kotlin data class."},"ProgressEntryStatusUpsertDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"color":{"type":"string"},"displayOrder":{"type":"number"},"active":{"type":"boolean"}},"required":["name"],"additionalProperties":false},"ProtocolFoodItemDto":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"createdBy":{"type":"string"},"name":{"type":"string"},"category":{"type":"string"},"subcategory":{"type":"string"},"aliases":{"type":"array","items":{"type":"string"}},"measurements":{"type":"array","items":{"$ref":"#/components/schemas/FoodMeasurement"}},"caloriesPerGram":{"type":"number"},"proteinPerGram":{"type":"number"},"fatPerGram":{"type":"number"},"carbsPerGram":{"type":"number"},"fiberPerGram":{"type":"number"},"sugarPerGram":{"type":"number"},"sodiumPerGram":{"type":"number"},"caloriesPer100g":{"type":"number"},"proteinPer100g":{"type":"number"},"fatPer100g":{"type":"number"},"carbsPer100g":{"type":"number"},"fiberPer100g":{"type":"number"},"sugarPer100g":{"type":"number"},"sodiumPer100g":{"type":"number"},"fdcId":{"type":"string"},"validated":{"type":"boolean"},"isLiquid":{"type":"boolean"},"dataSource":{"type":"string"},"aiEstimated":{"type":"boolean"},"searchStrategyUsed":{"type":"string"},"isSupplement":{"type":"boolean"},"primaryBenefit":{"type":"string"},"dosage":{"type":"string"},"timing":{"type":"string"},"frequency":{"type":"string"},"evidenceLevel":{"type":"string"},"priceRange":{"type":"string"},"targetAudience":{"type":"array","items":{"type":"string"}},"goals":{"type":"array","items":{"type":"string"}},"sideEffects":{"type":"array","items":{"type":"string"}},"contraindications":{"type":"array","items":{"type":"string"}},"supplementNotes":{"type":"string"},"isPublicLibrary":{"type":"boolean"}},"required":["isPublicLibrary"],"additionalProperties":false,"description":"Ports com.protocolcrm.api.dto.nutrition.ProtocolFoodItemDto and ProtocolFoodItemFiltersResponseDto.\n\nContract distinctions (verified against the Kotlin sources):  - ProtocolFoodItemDto IS"},"FoodMeasurement":{"type":"object","properties":{"name":{"type":["string","null"]},"grams":{"type":["number","null"]},"amount":{"type":["number","null"]}},"additionalProperties":false,"description":"Mirrors FoodMeasurement (@JsonInclude(NON_NULL)) — a JSONB sub-object."},"ProtocolFoodItemFiltersResponseDto":{"type":"object","properties":{"categories":{"type":"array","items":{"type":"string"}},"subcategories":{"type":"array","items":{"type":"string"}}},"required":["categories","subcategories"],"additionalProperties":false},"PublicAvailableSlotDto":{"type":"object","properties":{"startTime":{"type":"string"},"endTime":{"type":"string"},"isAvailable":{"type":"boolean"},"totalCapacity":{"type":["number","null"]},"busyCount":{"type":["number","null"]},"freeCount":{"type":["number","null"]}},"required":["startTime","endTime","isAvailable"],"additionalProperties":false},"PublicBookingRequestDto":{"type":"object","properties":{"startTime":{"type":"string"},"endTime":{"type":"string"},"clientName":{"type":"string"},"clientEmail":{"type":"string"},"clientPhone":{"type":["string","null"]},"attendeeEmails":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"notes":{"type":["string","null"]},"timezoneId":{"type":"string"}},"required":["startTime","endTime","clientName","clientEmail","timezoneId"],"additionalProperties":false},"PublicBookingResponseDto":{"type":"object","properties":{"id":{"type":"string"},"appointmentId":{"type":"string"},"startTime":{"type":"string"},"endTime":{"type":"string"},"status":{"type":"string"},"meetingTitle":{"type":"string"},"trainerName":{"type":"string"},"clientName":{"type":"string"},"clientEmail":{"type":"string"},"confirmationCode":{"type":["string","null"]}},"required":["id","appointmentId","startTime","endTime","status","meetingTitle","trainerName","clientName","clientEmail"],"additionalProperties":false},"PublicTenantResponseDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"code":{"type":["string","null"]},"tagline":{"type":["string","null"]},"logo":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"primaryBrandColor":{"type":["string","null"]},"secondaryBrandColor":{"type":["string","null"]},"cover":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"videoReel":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"description":{"type":["string","null"]},"labels":{"anyOf":[{"$ref":"#/components/schemas/Record<string,string>"},{"type":"null"}]}},"required":["id","name","code","tagline","logo","primaryBrandColor","secondaryBrandColor","cover","videoReel","description","labels"],"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.PublicTenantResponseDto (no"},"PurchaseDto":{"type":"object","properties":{"id":{"type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"userId":{"type":["string","null"]},"productId":{"type":["string","null"]},"code":{"type":["string","null"]},"name":{"type":["string","null"]},"url":{"type":["string","null"]},"status":{"anyOf":[{"$ref":"#/components/schemas/PurchaseStatus"},{"type":"null"}]},"purchasedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"expiresAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"canceledAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"pausedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"resumeAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"refundedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"amount":{"type":["number","null"]},"currency":{"type":["string","null"]},"quantity":{"type":["number","null"]},"pricingType":{"anyOf":[{"$ref":"#/components/schemas/PricingType"},{"type":"null"}]},"billingInterval":{"anyOf":[{"$ref":"#/components/schemas/BillingInterval"},{"type":"null"}]},"billingIntervalCount":{"type":["number","null"]},"billingEmail":{"type":["string","null"]},"billingName":{"type":["string","null"]},"taxRateId":{"type":["string","null"]},"taxRateName":{"type":["string","null"]},"taxRateValue":{"type":["number","null"]},"taxInclusive":{"type":["boolean","null"]},"taxAmount":{"type":["number","null"]},"totalSessions":{"type":["number","null"]},"sessionsConsumed":{"type":["number","null"]},"sessionsRemaining":{"type":["number","null"]},"stripeSubscriptionId":{"type":["string","null"]},"subscriptionCurrentPeriodEnd":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"cancelAtPeriodEnd":{"type":["boolean","null"]},"stripeCheckoutSessionId":{"type":["string","null"]},"stripePaymentIntentId":{"type":["string","null"]},"stripeCustomerId":{"type":["string","null"]},"externalId":{"type":["string","null"]},"externalSource":{"type":["string","null"]},"externalUpdatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"bundleProductId":{"type":["string","null"]},"programIds":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"metadata":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.shop.PurchaseDto (@JsonInclude(NON_NULL)).\n\nDeclaration order matters (Jackson emits in declaration order): BaseTenantDto contributes id, createdAt, updatedAt first, then the PurchaseDto fields. Used both as request body and response payload."},"PushTokenDto":{"type":"object","properties":{"id":{"type":["string","null"]},"token":{"type":["string","null"]},"deviceId":{"type":["string","null"]},"platform":{"type":["string","null"]},"deviceName":{"type":["string","null"]},"isActive":{"type":["boolean","null"]},"lastUsedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"userId":{"type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.notification.PushTokenDto (@JsonInclude(NON_NULL)). Null/undefined fields are stripped from the response. `platform` is a passthrough string (IOS | ANDROID | WEB)."},"RefreshTokenRequestDto":{"type":"object","properties":{"token":{"type":"string"}},"required":["token"],"additionalProperties":false},"RegisterPushTokenRequestDto":{"type":"object","properties":{"token":{"type":"string"},"deviceId":{"type":["string","null"]},"platform":{"type":["string","null"]},"deviceName":{"type":["string","null"]}},"required":["token"],"additionalProperties":false,"description":"Mirrors RegisterPushTokenRequestDto. `token` is required; the rest optional."},"RenewPurchaseRequest":{"type":"object","properties":{"amount":{"type":["number","null"]}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.shop.RenewPurchaseRequest."},"ResendVerificationRequestDto":{"type":"object","properties":{"email":{"type":"string"}},"additionalProperties":false},"ResolvedShopStyling":{"type":"object","properties":{"logo":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"color":{"type":["string","null"]},"logoSource":{"type":["string","null"]},"colorSource":{"type":["string","null"]}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.shop.ResolvedShopStyling (@JsonInclude(NON_NULL))."},"SearchFilter":{"type":"object","properties":{"q":{"type":"string"},"types":{"type":"array","items":{"$ref":"#/components/schemas/SearchResultType"}},"limit":{"type":"number"}},"required":["q","types","limit"],"additionalProperties":false},"SearchResultType":{"type":"string","enum":["CLIENT","PROGRAM","EXERCISE","NUTRITION_TEMPLATE"]},"SearchResponseDto":{"type":"object","properties":{"query":{"type":"string"},"results":{"type":"array","items":{"$ref":"#/components/schemas/SearchResultDto"}}},"required":["query","results"],"additionalProperties":false},"SearchResultDto":{"type":"object","properties":{"type":{"$ref":"#/components/schemas/SearchResultType"},"id":{"type":"string"},"title":{"type":"string"},"subtitle":{"type":"string"},"imageUrl":{"type":"string"}},"required":["type","id","title"],"additionalProperties":false},"SessionSpendDto":{"type":"object","properties":{"id":{"type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"purchaseId":{"type":["string","null"]},"spendDate":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"message":{"type":["string","null"]},"metadata":{"anyOf":[{"$ref":"#/components/schemas/Record<string,string>"},{"type":"null"}]}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.shop.SessionSpendDto (@JsonInclude(NON_NULL))."},"SharedMediaDto":{"type":"object","properties":{"share":{"$ref":"#/components/schemas/MediaShareDto"},"media":{"$ref":"#/components/schemas/MediaDto"}},"required":["share","media"],"additionalProperties":false},"ShopActiveStateDto":{"type":"object","properties":{"activeOneTimeCount":{"type":"number"},"activeSubscriptionCount":{"type":"number"},"expiringSoon":{"type":"array","items":{"$ref":"#/components/schemas/ExpiringPurchaseDto"}},"recentlyExpired":{"type":"array","items":{"$ref":"#/components/schemas/ExpiringPurchaseDto"}}},"required":["activeOneTimeCount","activeSubscriptionCount","expiringSoon","recentlyExpired"],"additionalProperties":false,"description":"ShopActiveStateDto (@JsonInclude(NON_NULL))."},"ShopConfigurationDto":{"type":"object","properties":{"id":{"type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"defaultTaxRateId":{"type":["string","null"]},"styling":{"anyOf":[{"$ref":"#/components/schemas/ShopStyling"},{"type":"null"}]},"businessDetails":{"anyOf":[{"$ref":"#/components/schemas/ShopBusinessDetails"},{"type":"null"}]},"invoiceFooterText":{"type":["string","null"]},"resolvedStyling":{"anyOf":[{"$ref":"#/components/schemas/ResolvedShopStyling"},{"type":"null"}]},"resolvedBusinessDetails":{"anyOf":[{"$ref":"#/components/schemas/ShopBusinessDetails"},{"type":"null"}]}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.shop.ShopConfigurationDto (@JsonInclude(NON_NULL)). Base id/createdAt/updatedAt first, then the configuration fields."},"ShopStyling":{"type":"object","properties":{"logo":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"color":{"type":["string","null"]}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.model.shop.ShopStyling (jsonb)."},"ShopBusinessDetails":{"type":"object","properties":{"businessName":{"type":["string","null"]},"address":{"type":["string","null"]},"vatNumber":{"type":["string","null"]},"email":{"type":["string","null"]},"phone":{"type":["string","null"]}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.model.shop.ShopBusinessDetails (jsonb)."},"ShopOverviewDto":{"type":"object","properties":{"periodData":{"anyOf":[{"$ref":"#/components/schemas/ShopPeriodDataDto"},{"type":"null"}]},"activeState":{"anyOf":[{"$ref":"#/components/schemas/ShopActiveStateDto"},{"type":"null"}]},"userPurchases":{"anyOf":[{"$ref":"#/components/schemas/Record<string,UserPurchasesDto>"},{"type":"null"}]}},"additionalProperties":false,"description":"ShopOverviewDto (@JsonInclude(NON_NULL)). userPurchases is a Kotlin Map<UUID, UserPurchasesDto> which Jackson serialises as a JSON object keyed by the UUID string."},"ShopPeriodDataDto":{"type":"object","properties":{"startDate":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"endDate":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"totalRevenue":{"type":"number"},"weeklyRevenue":{"type":"array","items":{"$ref":"#/components/schemas/WeeklyRevenueDto"}},"oneTimePurchaseCount":{"type":"number"},"newSubscriptionCount":{"type":"number"}},"required":["totalRevenue","weeklyRevenue","oneTimePurchaseCount","newSubscriptionCount"],"additionalProperties":false,"description":"ShopPeriodDataDto (@JsonInclude(NON_NULL))."},"WeeklyRevenueDto":{"type":"object","properties":{"weekStart":{"type":"string","format":"date-time"},"weekEnd":{"type":"string","format":"date-time"},"revenue":{"type":"number"}},"required":["weekStart","weekEnd","revenue"],"additionalProperties":false,"description":"WeeklyRevenueDto — NOT"},"Record<string,UserPurchasesDto>":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/UserPurchasesDto"}},"UserPurchasesDto":{"type":"object","properties":{"active":{"type":"array","items":{"$ref":"#/components/schemas/PurchaseDto"}},"past":{"type":"array","items":{"$ref":"#/components/schemas/PurchaseDto"}}},"required":["active","past"],"additionalProperties":false,"description":"UserPurchasesDto (@JsonInclude(NON_NULL))."},"SlotsQuery":{"type":"object","properties":{"startDate":{"type":"string"},"endDate":{"type":"string"},"timezoneId":{"type":"string"}},"required":["startDate","endDate","timezoneId"],"additionalProperties":false},"SpendSessionRequest":{"type":"object","properties":{"date":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"message":{"type":["string","null"]},"metadata":{"anyOf":[{"$ref":"#/components/schemas/Record<string,string>"},{"type":"null"}]}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.shop.SpendSessionRequest."},"StartWorkoutSessionRequestDto":{"type":"object","properties":{"clientSessionId":{"type":"string"},"workoutId":{"type":["string","null"]},"isCustom":{"type":["boolean","null"]},"customWorkoutName":{"type":["string","null"]}},"required":["clientSessionId"],"additionalProperties":false},"SubmitFeedbackRequestDto":{"type":"object","properties":{"subject":{"type":"string"},"message":{"type":"string"},"context":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]}},"required":["subject","message"],"additionalProperties":false,"description":"Mirrors SubmitFeedbackRequestDto."},"SubscriptionCheckoutResponse":{"type":"object","properties":{"checkoutUrl":{"type":"string"},"sessionId":{"type":"string"}},"required":["checkoutUrl","sessionId"],"additionalProperties":false,"description":"Response DTO for subscription checkout session creation."},"SubscriptionPlan":{"type":"string","enum":["INDIVIDUAL","TEAM","PROTOCOL_PRO"]},"SubscriptionStatus":{"type":"string","enum":["TRIALING","ACTIVE","PAST_DUE","CANCELED","UNPAID","INCOMPLETE","INCOMPLETE_EXPIRED"],"description":"Ports com.protocolcrm.api.dto.billing.PlatformBillingDto.kt.\n\nNONE of these DTOs carry"},"SubscriptionStatusResponse":{"type":"object","properties":{"hasActiveSubscription":{"type":"boolean"},"status":{"anyOf":[{"$ref":"#/components/schemas/SubscriptionStatus"},{"type":"null"}]},"plan":{"anyOf":[{"$ref":"#/components/schemas/SubscriptionPlan"},{"type":"null"}]},"billingInterval":{"anyOf":[{"$ref":"#/components/schemas/BillingInterval"},{"type":"null"}]},"currentPeriodEnd":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"cancelAtPeriodEnd":{"type":"boolean"},"activeClientCount":{"type":"number"},"includedClients":{"type":"number"},"overageClients":{"type":"number"},"totalSeats":{"type":"number"},"includedSeats":{"type":"number"},"additionalSeats":{"type":"number"},"currentSeatUsage":{"type":"number"},"canAddUsers":{"type":"boolean"},"aiCreditsUsed":{"type":"number"},"aiCreditsIncluded":{"type":"number"},"aiCreditOverageEnabled":{"type":"boolean"},"hasUsedTrial":{"type":"boolean"},"organizationMode":{"type":"string","enum":["SOLO","TEAM"]}},"required":["hasActiveSubscription","status","plan","billingInterval","currentPeriodEnd","cancelAtPeriodEnd","activeClientCount","includedClients","overageClients","totalSeats","includedSeats","additionalSeats","currentSeatUsage","canAddUsers","aiCreditsUsed","aiCreditsIncluded","aiCreditOverageEnabled","hasUsedTrial","organizationMode"],"additionalProperties":false,"description":"Response DTO for subscription status."},"SubtaskProgress":{"type":"object","properties":{"completed":{"type":"number"},"total":{"type":"number"}},"required":["completed","total"],"additionalProperties":false},"SubtaskReorderDto":{"type":"object","properties":{"subtaskIds":{"type":"array","items":{"type":"string"}}},"required":["subtaskIds"],"additionalProperties":false},"TaskBoardColumnCreateDto":{"type":"object","properties":{"name":{"type":"string"},"position":{"type":"number"},"color":{"type":"string"},"wipLimit":{"type":"number"},"isDoneColumn":{"type":"boolean"}},"required":["name"],"additionalProperties":false},"TaskBoardColumnDto":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"boardId":{"type":"string"},"name":{"type":"string"},"position":{"type":"number"},"color":{"type":"string"},"wipLimit":{"type":"number"},"isDoneColumn":{"type":"boolean"},"tasks":{"type":"array","items":{"$ref":"#/components/schemas/TaskDto"}},"taskCount":{"type":"number"}},"additionalProperties":false},"TaskDto":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"columnId":{"type":"string"},"boardId":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"position":{"type":"number"},"priority":{"type":"string"},"dueDate":{"type":"string"},"dueTime":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time"},"swimlane":{"type":"string"},"clientId":{"type":"string"},"client":{"$ref":"#/components/schemas/UserReferenceDto"},"createdById":{"type":"string"},"createdBy":{"$ref":"#/components/schemas/UserReferenceDto"},"assigneeIds":{"type":"array","items":{"type":"string"}},"assignees":{"type":"array","items":{"$ref":"#/components/schemas/UserReferenceDto"}},"labelIds":{"type":"array","items":{"type":"string"}},"labels":{"type":"array","items":{"$ref":"#/components/schemas/TaskLabelDto"}},"subtasks":{"type":"array","items":{"$ref":"#/components/schemas/TaskSubtaskDto"}},"subtaskProgress":{"$ref":"#/components/schemas/SubtaskProgress"},"isArchived":{"type":"boolean"},"isPrivate":{"type":"boolean"},"estimatedMinutes":{"type":"number"},"actualMinutes":{"type":"number"},"isOverdue":{"type":"boolean"},"recurrenceInterval":{"type":"number"},"recurrencePeriod":{"type":"string"},"sourceTaskId":{"type":"string"}},"additionalProperties":false},"UserReferenceDto":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"profilePicture":{}},"additionalProperties":false},"TaskLabelDto":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"name":{"type":"string"},"color":{"type":"string"},"description":{"type":"string"},"ownerId":{"type":"string"},"taskCount":{"type":"number"}},"additionalProperties":false},"TaskSubtaskDto":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"taskId":{"type":"string"},"title":{"type":"string"},"position":{"type":"number"},"isCompleted":{"type":"boolean"},"completedAt":{"type":"string","format":"date-time"},"completedById":{"type":"string"},"assigneeId":{"type":"string"}},"additionalProperties":false},"TaskBoardColumnUpdateDto":{"type":"object","properties":{"name":{"type":"string"},"color":{"type":"string"},"wipLimit":{"type":"number"},"isDoneColumn":{"type":"boolean"}},"additionalProperties":false},"TaskBoardCreateDto":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"ownerId":{"type":"string"},"color":{"type":"string"},"icon":{"type":"string"},"defaultColumns":{"type":"array","items":{"type":"string"}}},"required":["name"],"additionalProperties":false},"TaskBoardDto":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"name":{"type":"string"},"description":{"type":"string"},"ownerId":{"type":"string"},"isArchived":{"type":"boolean"},"color":{"type":"string"},"icon":{"type":"string"},"createdById":{"type":"string"},"columns":{"type":"array","items":{"$ref":"#/components/schemas/TaskBoardColumnDto"}},"taskCount":{"type":"number"}},"additionalProperties":false},"TaskBoardFilters":{"type":"object","properties":{"searchTerm":{"type":"string"},"ownerId":{"type":"string"},"isArchived":{"type":"boolean"},"createdById":{"type":"string"}},"additionalProperties":false},"TaskBoardListParams":{"type":"object","properties":{"page":{"type":"number"},"pageSize":{"type":"number"},"sortBy":{"type":"string"},"sortDir":{"type":"string","enum":["ASC","DESC"]},"filters":{"$ref":"#/components/schemas/TaskBoardFilters"}},"required":["page","pageSize","sortBy","sortDir","filters"],"additionalProperties":false},"TaskBoardUpdateDto":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"ownerId":{"type":"string"},"color":{"type":"string"},"icon":{"type":"string"},"isArchived":{"type":"boolean"}},"additionalProperties":false},"TaskCreateDto":{"type":"object","properties":{"columnId":{"type":"string"},"title":{"type":"string"},"description":{"type":"string"},"position":{"type":"number"},"priority":{"type":"string"},"dueDate":{"type":"string"},"dueTime":{"type":"string"},"swimlane":{"type":"string"},"clientId":{"type":["string","null"]},"assigneeIds":{"type":"array","items":{"type":"string"}},"labelIds":{"type":"array","items":{"type":"string"}},"estimatedMinutes":{"type":"number"},"subtasks":{"type":"array","items":{"$ref":"#/components/schemas/TaskSubtaskCreateDto"}},"completedAt":{"type":"string"},"isPrivate":{"type":"boolean"},"recurrenceInterval":{"type":"number"},"recurrencePeriod":{"type":"string"}},"required":["columnId","title"],"additionalProperties":false},"TaskSubtaskCreateDto":{"type":"object","properties":{"title":{"type":"string"},"position":{"type":"number"},"assigneeId":{"type":"string"}},"required":["title"],"additionalProperties":false},"TaskFilters":{"type":"object","properties":{"searchTerm":{"type":"string"},"boardId":{"type":"string"},"columnId":{"type":"string"},"assigneeId":{"type":"string"},"clientId":{"type":"string"},"labelIds":{"type":"array","items":{"type":"string"}},"priority":{"type":"string"},"dueAfter":{"type":"string"},"dueBefore":{"type":"string"},"isOverdue":{"type":"boolean"},"isCompleted":{"type":"boolean"},"isArchived":{"type":"boolean"},"swimlane":{"type":"string"},"createdById":{"type":"string"}},"additionalProperties":false},"TaskLabelCreateDto":{"type":"object","properties":{"name":{"type":"string"},"color":{"type":"string"},"description":{"type":"string"},"ownerId":{"type":"string"}},"required":["name","color"],"additionalProperties":false,"description":"Ports the input DTOs + list params for com.protocolcrm.api.dto.task.TaskLabelDto.kt.\n\nThe response type TaskLabelDto and its mapper (toTaskLabelDto) live in ../tasks/task.dto.ts (shared with the task surface). This file holds the label-specific request DTOs and the list query parser."},"TaskLabelFilters":{"type":"object","properties":{"searchTerm":{"type":"string"},"ownerId":{"type":"string"}},"additionalProperties":false},"TaskLabelListParams":{"type":"object","properties":{"page":{"type":"number"},"pageSize":{"type":"number"},"sortBy":{"type":"string"},"sortDir":{"type":"string","enum":["ASC","DESC"]},"filters":{"$ref":"#/components/schemas/TaskLabelFilters"}},"required":["page","pageSize","sortBy","sortDir","filters"],"additionalProperties":false},"TaskLabelUpdateDto":{"type":"object","properties":{"name":{"type":"string"},"color":{"type":"string"},"description":{"type":"string"},"ownerId":{"type":"string"}},"additionalProperties":false},"TaskListParams":{"type":"object","properties":{"page":{"type":"number"},"pageSize":{"type":"number"},"sortBy":{"type":"string"},"sortDir":{"type":"string","enum":["ASC","DESC"]},"filters":{"$ref":"#/components/schemas/TaskFilters"}},"required":["page","pageSize","sortBy","sortDir","filters"],"additionalProperties":false},"TaskMoveDto":{"type":"object","properties":{"columnId":{"type":"string"},"position":{"type":"number"}},"required":["columnId"],"additionalProperties":false},"TaskSubtaskUpdateDto":{"type":"object","properties":{"title":{"type":"string"},"isCompleted":{"type":"boolean"},"assigneeId":{"type":"string"}},"additionalProperties":false},"TaskUpdateDto":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"priority":{"type":"string"},"dueDate":{"type":"string"},"dueTime":{"type":"string"},"swimlane":{"type":"string"},"clientId":{"type":["string","null"]},"assigneeIds":{"type":"array","items":{"type":"string"}},"labelIds":{"type":"array","items":{"type":"string"}},"estimatedMinutes":{"type":"number"},"actualMinutes":{"type":"number"},"isArchived":{"type":"boolean"},"isPrivate":{"type":"boolean"},"recurrenceInterval":{"type":"number"},"recurrencePeriod":{"type":"string"}},"additionalProperties":false},"TaxRateDto":{"type":"object","properties":{"id":{"type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"string"},{"type":"null"}]},"name":{"type":["string","null"]},"rate":{"type":["number","null"]},"isInclusive":{"type":"boolean"},"isActive":{"type":"boolean"},"stripeTaxRateId":{"type":["string","null"]},"syncWithStripe":{"type":["boolean","null"]}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.shop.TaxRateDto (@JsonInclude(NON_NULL)).\n\nBase id/createdAt/updatedAt declared first, then TaxRateDto fields. `rate` is a BigDecimal in Kotlin (e.g. 0.20) → bare JSON number. isInclusive/isActive are non-null primitives (always emitted); syncWithStripe is nullable (default true)."},"TeamInviteDto":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"teamPermissions":{"type":"array","items":{"$ref":"#/components/schemas/TeamPermission"}},"status":{"$ref":"#/components/schemas/InviteStatus"},"expiresAt":{"type":"string","format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"invitedByName":{"type":"string"},"isExpired":{"type":"boolean"}},"additionalProperties":false},"InviteStatus":{"type":"string","enum":["PENDING","ACCEPTED","EXPIRED","REVOKED"],"description":"Mirrors com.protocolcrm.api.model.team.TeamInvite (table: team_invites).\n\nIn Kotlin this extends BaseEntity (id-only, app-generated UUID) and declares its own tenant_id / created_at / updated_at columns — it does NOT extend BaseTenantEntity. So we model it standalone here. `id`, `created_at` and `updated_at` carry no DB defaults (Hibernate + Spring Data auditing populated them app-side), so we assign them in"},"TeamInviteRequestDto":{"type":"object","properties":{"email":{"type":"string"},"teamPermissions":{"type":"array","items":{"$ref":"#/components/schemas/TeamPermission"}},"firstName":{"type":["string","null"]},"lastName":{"type":["string","null"]}},"required":["email","teamPermissions"],"additionalProperties":false},"TeamMemberDto":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"profilePicture":{},"teamPermissions":{"type":"array","items":{"$ref":"#/components/schemas/TeamPermission"}},"isOwner":{"type":"boolean"},"joinedAt":{"type":"string","format":"date-time"}},"additionalProperties":false},"TenantRequestDto":{"type":"object","properties":{"name":{"type":"string"}},"required":["name"],"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.TenantRequestDto — POST body for create."},"TenantResponseDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"code":{"type":["string","null"]},"users":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/TenantUserDto"}},{"type":"null"}]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"tagline":{"type":["string","null"]},"logo":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"primaryBrandColor":{"type":["string","null"]},"secondaryBrandColor":{"type":["string","null"]},"monthlyClientTarget":{"type":["number","null"]},"primaryBusinessGoal":{"type":["string","null"]},"businessOnboardingCompletedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"cover":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"videoReel":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"description":{"type":["string","null"]},"labels":{"anyOf":[{"$ref":"#/components/schemas/Record<string,string>"},{"type":"null"}]},"isPubliclyVisible":{"type":["boolean","null"]},"subscriptionStatus":{"type":["string","null"]},"billingInterval":{"type":["string","null"]},"subscriptionCurrentPeriodEnd":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"subscriptionCancelAtPeriodEnd":{"type":["boolean","null"]},"subscriptionPlan":{"type":["string","null"]},"seatCount":{"type":["number","null"]},"currentSeatUsage":{"type":["number","null"]},"includedClients":{"type":["number","null"]},"includedSeats":{"type":["number","null"]},"additionalSeats":{"type":["number","null"]},"aiCreditsUsed":{"type":["number","null"]},"aiCreditOverageEnabled":{"type":["boolean","null"]},"notificationConfig":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]},"coachingProfile":{"type":["string","null"]}},"required":["id","name","code","users","createdAt","updatedAt","tagline","logo","primaryBrandColor","secondaryBrandColor","monthlyClientTarget","primaryBusinessGoal","businessOnboardingCompletedAt","cover","videoReel","description","labels","isPubliclyVisible","subscriptionStatus","billingInterval","subscriptionCurrentPeriodEnd","subscriptionCancelAtPeriodEnd","subscriptionPlan","seatCount","currentSeatUsage","includedClients","includedSeats","additionalSeats","aiCreditsUsed","aiCreditOverageEnabled","notificationConfig","coachingProfile"],"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.TenantResponseDto. The Kotlin data class has NO"},"TenantUserDto":{"type":"object","properties":{"id":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"phoneNumber":{"type":"string"},"profilePicture":{"$ref":"#/components/schemas/Record<string,unknown>"},"role":{"type":"string"},"isActive":{"type":"boolean"},"dataProfilesCreated":{"type":"boolean"}},"additionalProperties":false,"description":"The lightweight UserDto the Kotlin TenantService builds for tenant.users (a SUBSET of the full UserDto: only id/firstName/lastName/email/phoneNumber/profilePicture/ role/isActive are set). The full UserDto is"},"TenantUpdateRequestDto":{"type":"object","properties":{"name":{"type":["string","null"]},"tagline":{"type":["string","null"]},"logo":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"primaryBrandColor":{"type":["string","null"]},"secondaryBrandColor":{"type":["string","null"]},"monthlyClientTarget":{"type":["number","null"]},"primaryBusinessGoal":{"type":["string","null"]},"cover":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"videoReel":{"anyOf":[{"$ref":"#/components/schemas/MediaDto"},{"type":"null"}]},"description":{"type":["string","null"]},"labels":{"anyOf":[{"$ref":"#/components/schemas/Record<string,string>"},{"type":"null"}]},"isPubliclyVisible":{"type":["boolean","null"]},"notificationConfig":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]},"coachingProfile":{"type":["string","null"]}},"additionalProperties":false},"TokenDto":{"type":"object","properties":{"accessToken":{"type":["string","null"]},"tokenType":{"type":"string"},"expiresAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"status":{"$ref":"#/components/schemas/TokenStatus"},"message":{"type":["string","null"]},"redirectUrl":{"type":["string","null"]},"emailVerified":{"type":["boolean","null"]},"user":{"anyOf":[{"$ref":"#/components/schemas/UserDto"},{"type":"null"}]},"role":{"anyOf":[{"$ref":"#/components/schemas/UserRole"},{"type":"null"}]}},"required":["accessToken","tokenType","expiresAt","status","message","redirectUrl","emailVerified","user","role"],"additionalProperties":false,"description":"Mirrors TokenDto (NOT NON_NULL — nulls are serialized, matching Kotlin)."},"TokenStatus":{"type":"string","enum":["SUCCESS","WAITING_FOR_CODE","INVALID_CREDENTIALS","RATE_LIMITED","CODE_EXPIRED","INVALID_CODE","ERROR","EMAIL_NOT_VERIFIED","USER_NOT_FOUND","USER_INACTIVE"],"description":"Mirrors com.protocolcrm.api.dto.user.TokenStatus."},"TranslationRequestDto":{"type":"object","properties":{"texts":{"type":"array","items":{"$ref":"#/components/schemas/TranslationText"}},"targetLocale":{"type":"string"},"sourceLocale":{"type":["string","null"]}},"required":["texts","targetLocale"],"additionalProperties":false,"description":"Mirrors TranslationRequest."},"TranslationText":{"type":"object","properties":{"key":{"type":"string"},"value":{"type":"string"}},"required":["key","value"],"additionalProperties":false,"description":"Mirrors TranslationText (dto/translation)."},"TranslationResponseDto":{"type":"object","properties":{"translations":{"type":"array","items":{"$ref":"#/components/schemas/TranslationResultDto"}},"targetLocale":{"type":"string"}},"required":["translations","targetLocale"],"additionalProperties":false,"description":"Mirrors TranslationResponse."},"TranslationResultDto":{"type":"object","properties":{"key":{"type":"string"},"originalValue":{"type":"string"},"translatedValue":{"type":"string"}},"required":["key","originalValue","translatedValue"],"additionalProperties":false,"description":"Mirrors TranslationResult."},"TutorialItemDto":{"type":"object","properties":{"id":{"type":"string"},"userId":{"type":"string"},"type":{"type":"string"},"message":{"type":"string"},"isCompleted":{"type":"boolean"},"completedAt":{"type":"string","format":"date-time"},"displayOrder":{"type":"number"},"category":{"type":"string"},"metadata":{"$ref":"#/components/schemas/Record<string,unknown>"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"isTimeSensitive":{"type":"boolean"},"expiresAt":{"type":"string","format":"date-time"},"isExpired":{"type":"boolean"}},"required":["isCompleted","isTimeSensitive","isExpired"],"additionalProperties":false,"description":"Ports com.protocolcrm.api.dto.tutorial.TutorialItemDto.kt.\n\nTutorialItemDto is"},"TutorialItemFilterParamsDto":{"type":"object","properties":{"role":{"anyOf":[{"$ref":"#/components/schemas/UserRole"},{"type":"null"}]},"type":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"isCompleted":{"type":["boolean","null"]},"createdAfter":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"createdBefore":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"page":{"type":"number"},"pageSize":{"type":"number"}},"required":["role","type","isCompleted","createdAfter","createdBefore","page","pageSize"],"additionalProperties":false,"description":"Filter params for the trainer-facing aggregation (GET /aggregated). page/pageSize are 1-indexed with defaults 1/20 (mirrors the"},"TutorialItemListParams":{"type":"object","properties":{"incompleteOnly":{"type":["boolean","null"]},"userId":{"type":["string","null"]}},"required":["incompleteOnly","userId"],"additionalProperties":false,"description":"Params for the legacy single-user GET (incompleteOnly + userId)."},"UpdateAdditionalSeatsRequest":{"type":"object","properties":{"additionalSeats":{"type":"number"}},"required":["additionalSeats"],"additionalProperties":false,"description":"Request DTO for updating additional seats beyond the 1 included."},"UpdateAdditionalSeatsResponse":{"type":"object","properties":{"previousAdditionalSeats":{"type":"number"},"newAdditionalSeats":{"type":"number"},"totalSeats":{"type":"number"},"effectiveDate":{"type":"string","format":"date-time"}},"required":["previousAdditionalSeats","newAdditionalSeats","totalSeats","effectiveDate"],"additionalProperties":false,"description":"Response DTO for additional seats update."},"UpdateAutomationRequest":{"type":"object","properties":{"name":{"type":["string","null"]},"config":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]},"triggerConfig":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]}},"additionalProperties":false},"UpdateMediaShareRequestDto":{"type":"object","properties":{"permission":{"anyOf":[{"$ref":"#/components/schemas/SharePermission"},{"type":"null"}]},"expiresAt":{"type":["string","null"]},"isActive":{"type":["boolean","null"]}},"additionalProperties":false},"UpdateNutritionLogDto":{"type":"object","properties":{"date":{"type":["string","null"]},"time":{"type":["string","null"]},"items":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/NutritionLogItem"}},{"type":"null"}]},"notes":{"type":["string","null"]},"nutritionTemplateId":{"type":["string","null"]}},"additionalProperties":false},"UpdatePermissionsRequestDto":{"type":"object","properties":{"teamPermissions":{"type":"array","items":{"$ref":"#/components/schemas/TeamPermission"}}},"required":["teamPermissions"],"additionalProperties":false},"UpdateWorkoutSessionRequestDto":{"type":"object","properties":{"status":{"type":["string","null"]},"difficultyRating":{"type":["number","null"]},"enjoymentRating":{"type":["number","null"]},"energyLevelAfter":{"type":["number","null"]},"notes":{"type":["string","null"]},"exercisesCompleted":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/SessionExerciseDto"}},{"type":"null"}]}},"additionalProperties":false},"UpsertConfigRequestDto":{"type":"object","properties":{"key":{"type":"string"},"data":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]},"tags":{"type":"array","items":{"type":"string"}},"tenantLevel":{"type":"boolean"}},"required":["key"],"additionalProperties":false,"description":"Mirrors UpsertConfigRequestDto."},"UsageSummaryResponse":{"type":"object","properties":{"activeClientCount":{"type":"number"},"includedClients":{"type":"number"},"overageClients":{"type":"number"},"overageClientsCost":{"type":"number"},"totalSeats":{"type":"number"},"includedSeats":{"type":"number"},"additionalSeats":{"type":"number"},"additionalSeatsCost":{"type":"number"},"currentSeatUsage":{"type":"number"},"aiCreditsUsed":{"type":"number"},"aiCreditsIncluded":{"type":"number"},"overageCreditPacks":{"type":"number"},"overageCreditsCost":{"type":"number"},"aiCreditOverageEnabled":{"type":"boolean"},"baseCost":{"type":"number"},"estimatedMonthlyTotal":{"type":"number"},"currentPeriodEnd":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]}},"required":["activeClientCount","includedClients","overageClients","overageClientsCost","totalSeats","includedSeats","additionalSeats","additionalSeatsCost","currentSeatUsage","aiCreditsUsed","aiCreditsIncluded","overageCreditPacks","overageCreditsCost","aiCreditOverageEnabled","baseCost","estimatedMonthlyTotal","currentPeriodEnd"],"additionalProperties":false,"description":"Response DTO for usage summary with cost estimates."},"UserBehavioralProfileResponseDto":{"type":"object","properties":{"userId":{"type":"string"},"notes":{"type":["string","null"]},"motivationFactors":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/MotivationFactor"}},{"type":"null"}]},"adherenceRate":{"type":["number","null"]},"consistencyScore":{"type":["number","null"]},"preferredCommunicationStyles":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/CommunicationStyle"}},{"type":"null"}]},"preferredCommunicationFrequency":{"anyOf":[{"$ref":"#/components/schemas/CommunicationFrequency"},{"type":"null"}]},"preferredCommunicationChannels":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/CommunicationChannel"}},{"type":"null"}]},"preferredFeedbackStyles":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/FeedbackStyle"}},{"type":"null"}]},"responsesToChallenges":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ChallengeResponse"}},{"type":"null"}]},"behavioralTriggers":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]}},"additionalProperties":false},"UserCreateRequestDto":{"type":"object","properties":{"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"phoneNumber":{"type":"string"},"instagram":{"type":"string"},"facebook":{"type":"string"},"tiktok":{"type":"string"},"trainerId":{"type":"string"},"role":{"$ref":"#/components/schemas/UserRole"},"address":{"$ref":"#/components/schemas/Record<string,unknown>"},"externalId":{"type":"string"},"language":{"type":"string"},"unitSystem":{"type":"string"},"defaultCurrency":{"type":"string"},"sendAppAccessInstructions":{"type":"boolean"},"dataCollectionMethod":{"$ref":"#/components/schemas/DataCollectionMethod"},"onboardingFormId":{"type":"string"}},"additionalProperties":false},"DataCollectionMethod":{"type":"string","enum":["MANUAL","ASK_CLIENT","USE_TRANSCRIPT","CUSTOM"],"description":"Mirrors com.protocolcrm.api.dto.user.UserCreateRequestDto. Subset that's actually consumed by the Node create() — everything else (manual-data setup, transcripts, scheduleCheckins, lead-conversion) is intentionally not ported until the downstream services land."},"UserFilterParams":{"type":"object","properties":{"role":{"$ref":"#/components/schemas/UserRole"},"search":{"type":"string"},"trainerId":{"type":"array","items":{"type":"string"}},"isActive":{"type":"boolean"},"email":{"type":"string"},"externalId":{"type":"string"},"tenantId":{"type":"string"},"lifecycleStageId":{"type":"array","items":{"type":"string"}},"page":{"type":"number"},"pageSize":{"type":"number"},"sortBy":{"type":"string"},"sortDir":{"type":"string","enum":["ASC","DESC"]}},"required":["page","pageSize","sortBy","sortDir"],"additionalProperties":false},"UserHealthProfileResponseDto":{"type":"object","properties":{"dateOfBirth":{"type":["string","null"]},"gender":{"anyOf":[{"$ref":"#/components/schemas/Gender"},{"type":"null"}]},"heightCm":{"type":["number","null"]},"notes":{"type":["string","null"]},"allergies":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"clientDeniesAllergies":{"type":["boolean","null"]},"previousInjuries":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"clientDeniesPreviousInjuries":{"type":["boolean","null"]},"lifestyleFactors":{"anyOf":[{"$ref":"#/components/schemas/Record<string,unknown>"},{"type":"null"}]},"riskAssessment":{"anyOf":[{"$ref":"#/components/schemas/HealthRiskLevel"},{"type":"null"}]},"medicalConditions":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/MedicalConditionDto"}},{"type":"null"}]},"clientDeniesMedicalConditions":{"type":["boolean","null"]},"assessmentResults":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/PhysicalAssessmentResultDto"}},{"type":"null"}]}},"additionalProperties":false},"UserReOnboardRequestDto":{"type":"object","properties":{"dataCollectionMethod":{"$ref":"#/components/schemas/DataCollectionMethod"},"onboardingFormId":{"type":"string"},"customInstructions":{"type":"string"},"phoneNumber":{"type":"string"},"sendAppAccessInstructions":{"type":"boolean"}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.user.UserReOnboardRequestDto — the body of POST /v1/users/:userId/re-onboard. Same shape the web client (ReOnboardRequest) sends."},"UserSummaryDto":{"type":"object","properties":{"healthProfile":{"anyOf":[{"$ref":"#/components/schemas/UserHealthProfileResponseDto"},{"type":"null"}]},"fitnessProfile":{"anyOf":[{"$ref":"#/components/schemas/UserFitnessProfileDto"},{"type":"null"}]},"nutritionProfile":{"anyOf":[{"$ref":"#/components/schemas/UserNutritionProfileDto"},{"type":"null"}]},"behavioralProfile":{"anyOf":[{"$ref":"#/components/schemas/UserBehavioralProfileResponseDto"},{"type":"null"}]},"latestProgress":{"anyOf":[{"$ref":"#/components/schemas/ProgressEntryDto"},{"type":"null"}]}},"additionalProperties":false,"description":"Mirrors com.protocolcrm.api.dto.user.UserSummaryDto (@JsonInclude(NON_NULL)). Aggregates the four user profiles plus the most recent progress entry — pure data assembly, no LLM. Null fields are omitted from the response."},"UserUpdateDto":{"type":"object","properties":{"email":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"phoneNumber":{"type":"string"},"instagram":{"type":"string"},"facebook":{"type":"string"},"tiktok":{"type":"string"},"profilePicture":{"$ref":"#/components/schemas/Record<string,unknown>"},"address":{"$ref":"#/components/schemas/Record<string,unknown>"},"language":{"type":"string"},"unitSystem":{"type":"string"},"defaultCurrency":{"type":"string"},"lifecycleStageId":{"type":["string","null"]},"isActive":{"type":"boolean"},"externalId":{"type":["string","null"]},"externalUpdatedAt":{"type":["string","null"]}},"additionalProperties":false,"description":"Fields accepted on update (PUT /v1/users/:id and /me)."},"VerifyEmailRequestDto":{"type":"object","properties":{"code":{"type":"string"}},"additionalProperties":false,"description":"Body of POST /verify-email and /resend-verification (Kotlin Map<String,String>)."},"WodpressProductPurchaseInfo":{"type":"object","properties":{"id":{"type":["number","null"]},"product_id":{"type":"number"},"sku":{"type":["string","null"]},"name":{"type":["string","null"]},"quantity":{"type":["number","null"]}},"required":["product_id"],"additionalProperties":false},"WooBillingDto":{"type":"object","properties":{"first_name":{"type":["string","null"]},"last_name":{"type":["string","null"]},"email":{"type":["string","null"]}},"additionalProperties":false,"description":"DTOs for the WordPress / WooCommerce webhook surface.\n\nRequest shapes mirror com.protocolcrm.api.dto.integrations.wordpress.WooCommerceDTO and the inline data classes in WordpressWebhookController.kt. Field names use the WooCommerce/WordPress snake_case wire format verbatim (Jackson binds by property name). Only the fields the controller actually reads are required to be correct; the rest are passthrough.\n\nResponse shapes are NOT"},"WooCommerceOrderDto":{"type":"object","properties":{"id":{"type":["number","null"]},"status":{"type":["string","null"]},"billing":{"anyOf":[{"$ref":"#/components/schemas/WooBillingDto"},{"type":"null"}]},"line_items":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/WooLineItemDto"}},{"type":"null"}]}},"additionalProperties":false,"description":"Subset of WooCommerceOrderDto the controller reads (id, status, billing, line_items)."},"WooLineItemDto":{"type":"object","properties":{"id":{"type":["number","null"]},"product_id":{"type":["number","null"]},"quantity":{"type":["number","null"]}},"additionalProperties":false},"WordpressPurchaseInfo":{"type":"object","properties":{"order_id":{"type":"number"},"payment_reference":{"type":["string","null"]},"customer_email":{"type":["string","null"]},"customer_name":{"type":["string","null"]},"products":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/WodpressProductPurchaseInfo"}},{"type":"null"}]}},"required":["order_id"],"additionalProperties":false},"WordpressPurchaseResponse":{"type":"object","properties":{"code":{"type":"string"},"url":{"type":"string"}},"required":["code","url"],"additionalProperties":false},"WordpressValidatePurchaseResponse":{"type":"object","properties":{"valid":{"type":"boolean"}},"required":["valid"],"additionalProperties":false},"WorkoutSearchFiltersDto":{"type":"object","properties":{"page":{"type":"number"},"pageSize":{"type":"number"},"sortBy":{"type":"string"},"sortDir":{"type":"string","enum":["ASC","DESC"]},"isTemplate":{"type":["boolean","null"]},"isPublicLibrary":{"type":["boolean","null"]},"experienceLevel":{"anyOf":[{"$ref":"#/components/schemas/ExperienceLevel"},{"type":"null"}]},"difficulty":{"anyOf":[{"$ref":"#/components/schemas/WorkoutDifficulty"},{"type":"null"}]},"searchTerm":{"type":["string","null"]},"goals":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/TrainingGoal"}},{"type":"null"}]},"muscleGroups":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/MuscleGroup"}},{"type":"null"}]},"equipment":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/Equipment"}},{"type":"null"}]},"durationMin":{"type":["number","null"]},"durationMax":{"type":["number","null"]},"userIds":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"ownerId":{"type":["string","null"]},"scope":{"anyOf":[{"$ref":"#/components/schemas/LibraryScopeParam"},{"type":"null"}],"description":"Explicit scope param (mine|team|all) — preferred over the legacy ownerId sentinel."},"categoryId":{"type":["string","null"]},"externalId":{"type":["string","null"]}},"required":["page","pageSize","sortBy","sortDir","isTemplate","isPublicLibrary","experienceLevel","difficulty","searchTerm","goals","muscleGroups","equipment","durationMin","durationMax","userIds","ownerId","categoryId","externalId"],"additionalProperties":false},"LibraryScopeParam":{"type":"string","enum":["mine","team","all"],"description":"Explicit list-scope query param. Replaces the `ownerId === tenantId` magic sentinel."},"WorkoutSessionDto":{"type":"object","properties":{"id":{"type":["string","null"]},"createdAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"updatedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"userId":{"type":"string"},"workoutId":{"type":["string","null"]},"workoutName":{"type":["string","null"]},"isCustom":{"type":["boolean","null"]},"customWorkoutName":{"type":["string","null"]},"startedAt":{"type":"string","format":"date-time"},"completedAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"durationMinutes":{"type":["number","null"]},"status":{"type":"string"},"difficultyRating":{"type":["number","null"]},"enjoymentRating":{"type":["number","null"]},"energyLevelBefore":{"type":["number","null"]},"energyLevelAfter":{"type":["number","null"]},"notes":{"type":["string","null"]},"caloriesBurnedEstimate":{"type":["number","null"]},"exercisesCompleted":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/SessionExerciseDto"}},{"type":"null"}]},"clientSessionId":{"type":["string","null"]}},"required":["id","createdAt","updatedAt","userId","workoutId","workoutName","isCustom","customWorkoutName","startedAt","completedAt","durationMinutes","status","difficultyRating","enjoymentRating","energyLevelBefore","energyLevelAfter","notes","caloriesBurnedEstimate","exercisesCompleted","clientSessionId"],"additionalProperties":false},"WorkoutSessionFiltersDto":{"type":"object","properties":{"page":{"type":"number"},"pageSize":{"type":"number"},"sortBy":{"type":"string"},"sortDir":{"type":"string","enum":["ASC","DESC"]},"userId":{"type":["string","null"]},"workoutIds":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"status":{"type":["string","null"]},"startDate":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"endDate":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"minDuration":{"type":["number","null"]},"maxDuration":{"type":["number","null"]},"minDifficultyRating":{"type":["number","null"]},"maxDifficultyRating":{"type":["number","null"]},"minEnjoymentRating":{"type":["number","null"]},"maxEnjoymentRating":{"type":["number","null"]}},"required":["page","pageSize","sortBy","sortDir","userId","workoutIds","status","startDate","endDate","minDuration","maxDuration","minDifficultyRating","maxDifficultyRating","minEnjoymentRating","maxEnjoymentRating"],"additionalProperties":false},"WorkoutSessionSummaryDto":{"type":"object","properties":{"userId":{"type":"string"},"startDate":{"type":"string","format":"date-time"},"endDate":{"type":"string","format":"date-time"},"totalSessions":{"type":"number"},"totalDurationMinutes":{"type":"number"},"totalCaloriesBurned":{"type":"number"},"mostTrainedMuscleGroups":{"type":"array","items":{"type":"string"}},"averageDifficultyRating":{"type":["number","null"]},"averageEnjoymentRating":{"type":["number","null"]},"completionRate":{"type":"number"},"performanceTrends":{"$ref":"#/components/schemas/Record<string,Record<string,unknown>>"}},"required":["userId","startDate","endDate","totalSessions","totalDurationMinutes","totalCaloriesBurned","mostTrainedMuscleGroups","averageDifficultyRating","averageEnjoymentRating","completionRate","performanceTrends"],"additionalProperties":false},"Record<string,Record<string,unknown>>":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Record<string,unknown>"}}},"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}}},"info":{"title":"Protocol API","version":"0.1.0"},"openapi":"3.0.0","paths":{"/v1/auth/login":{"post":{"operationId":"AuthController.login","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequestDto"},"example":{"email":"coach@example.com","password":"S3curePass!"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TokenDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"User authenticated successfully","data":{"accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIzZjlhMWMyZS04YjdkLTRlNmYtOWEwYi0xYzJkM2U0ZjVhNmIiLCJyb2xlIjoiVFJBSU5FUiIsImlhdCI6MTc2ODQ5MjIwMH0.4y8Xq2mJ1hK9pQ7wZ3nR5tB6cV0dF1gH2jK3lM4nO5","tokenType":"Bearer","expiresAt":"2026-01-15T21:30:00.000Z","status":"SUCCESS","message":"User authenticated successfully","redirectUrl":null,"emailVerified":true,"user":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","email":"coach@example.com","firstName":"Jordan","lastName":"Rivera","role":"TRAINER","isActive":true,"emailVerified":true,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"},"role":"TRAINER"}}}},"description":"Success"}},"summary":"Authenticate with email + password (or client auth code)","tags":["Auth"]}},"/v1/auth/refresh-token":{"post":{"operationId":"AuthController.refresh","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshTokenRequestDto"},"example":{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIzZjlhMWMyZS04YjdkLTRlNmYtOWEwYi0xYzJkM2U0ZjVhNmIiLCJyb2xlIjoiVFJBSU5FUiIsImlhdCI6MTc2ODQ5MjIwMH0.4y8Xq2mJ1hK9pQ7wZ3nR5tB6cV0dF1gH2jK3lM4nO5"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TokenDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Token refreshed successfully","data":{"accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIzZjlhMWMyZS04YjdkLTRlNmYtOWEwYi0xYzJkM2U0ZjVhNmIiLCJyb2xlIjoiVFJBSU5FUiIsImlhdCI6MTc2ODQ5MjIwMH0.4y8Xq2mJ1hK9pQ7wZ3nR5tB6cV0dF1gH2jK3lM4nO5","tokenType":"Bearer","expiresAt":"2026-01-15T21:30:00.000Z","status":"SUCCESS","message":"User authenticated successfully","redirectUrl":null,"emailVerified":true,"user":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","email":"coach@example.com","firstName":"Jordan","lastName":"Rivera","role":"TRAINER","isActive":true,"emailVerified":true,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"},"role":"TRAINER"}}}},"description":"Success"}},"summary":"Exchange a valid token for a fresh access token","tags":["Auth"]}},"/v1/auth/register":{"post":{"operationId":"AuthController.register","requestBody":{"content":{"application/json":{"schema":{},"example":{"email":"coach@example.com","firstName":"Jordan","lastName":"Rivera","password":"S3curePass!"}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TokenDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Registration successful! Please check your email to verify your account.","data":{"accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIzZjlhMWMyZS04YjdkLTRlNmYtOWEwYi0xYzJkM2U0ZjVhNmIiLCJyb2xlIjoiVFJBSU5FUiIsImlhdCI6MTc2ODQ5MjIwMH0.4y8Xq2mJ1hK9pQ7wZ3nR5tB6cV0dF1gH2jK3lM4nO5","tokenType":"Bearer","expiresAt":"2026-01-15T21:30:00.000Z","status":"SUCCESS","message":"User authenticated successfully","redirectUrl":null,"emailVerified":false,"user":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","email":"coach@example.com","firstName":"Jordan","lastName":"Rivera","role":"TRAINER","isActive":true,"emailVerified":true,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"},"role":"TRAINER"}}}},"description":"Success"}},"summary":"Register a new trainer account (self-serve signup)","tags":["Auth"]}},"/v1/auth/signup":{"post":{"operationId":"AuthController.signup","requestBody":{"content":{"application/json":{"schema":{},"example":{"email":"coach@example.com","firstName":"Jordan","lastName":"Rivera","password":"S3curePass!"}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TokenDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Registration successful! Please check your email to verify your account.","data":{"accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIzZjlhMWMyZS04YjdkLTRlNmYtOWEwYi0xYzJkM2U0ZjVhNmIiLCJyb2xlIjoiVFJBSU5FUiIsImlhdCI6MTc2ODQ5MjIwMH0.4y8Xq2mJ1hK9pQ7wZ3nR5tB6cV0dF1gH2jK3lM4nO5","tokenType":"Bearer","expiresAt":"2026-01-15T21:30:00.000Z","status":"SUCCESS","message":"User authenticated successfully","redirectUrl":null,"emailVerified":false,"user":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","email":"coach@example.com","firstName":"Jordan","lastName":"Rivera","role":"TRAINER","isActive":true,"emailVerified":true,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"},"role":"TRAINER"}}}},"description":"Success"}},"summary":"Sign up a new trainer (V1 MVP: role forced to TRAINER)","tags":["Auth"]}},"/v1/auth/client-signup":{"post":{"operationId":"AuthController.clientSignup","requestBody":{"content":{"application/json":{"schema":{},"example":{"email":"client@example.com","firstName":"Alex","lastName":"Kim","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TokenDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Registration successful! Please check your email to verify your account.","data":{"accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIzZjlhMWMyZS04YjdkLTRlNmYtOWEwYi0xYzJkM2U0ZjVhNmIiLCJyb2xlIjoiVFJBSU5FUiIsImlhdCI6MTc2ODQ5MjIwMH0.4y8Xq2mJ1hK9pQ7wZ3nR5tB6cV0dF1gH2jK3lM4nO5","tokenType":"Bearer","expiresAt":"2026-01-15T21:30:00.000Z","status":"SUCCESS","message":"User authenticated successfully","redirectUrl":null,"emailVerified":false,"user":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","email":"client@example.com","firstName":"Jordan","lastName":"Rivera","role":"CLIENT","isActive":true,"emailVerified":true,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"},"role":"CLIENT"}}}},"description":"Success"}},"summary":"Self-register a client into an existing tenant","tags":["Auth"]}},"/v1/auth/verify-email":{"post":{"operationId":"AuthController.verifyEmail","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyEmailRequestDto"},"example":{"code":"123456"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Email verified successfully!","data":{"accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIzZjlhMWMyZS04YjdkLTRlNmYtOWEwYi0xYzJkM2U0ZjVhNmIiLCJyb2xlIjoiVFJBSU5FUiIsImlhdCI6MTc2ODQ5MjIwMH0.4y8Xq2mJ1hK9pQ7wZ3nR5tB6cV0dF1gH2jK3lM4nO5","tokenType":"Bearer","expiresAt":"2026-01-15T21:30:00.000Z","status":"SUCCESS","message":"User authenticated successfully","redirectUrl":null,"emailVerified":true,"user":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","email":"coach@example.com","firstName":"Jordan","lastName":"Rivera","role":"TRAINER","isActive":true,"emailVerified":true,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"},"role":"TRAINER"}}}},"description":"Success"}},"summary":"Verify an email address with the emailed code","tags":["Auth"]}},"/v1/auth/resend-verification":{"post":{"operationId":"AuthController.resendVerification","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResendVerificationRequestDto"},"example":{"email":"coach@example.com"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Verification email sent","data":{"accessToken":null,"tokenType":"Bearer","expiresAt":"2026-01-15T21:30:00.000Z","status":"SUCCESS","message":"Verification email sent","redirectUrl":null,"emailVerified":false,"user":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","email":"coach@example.com","firstName":"Jordan","lastName":"Rivera","role":"TRAINER","isActive":true,"emailVerified":true,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"},"role":"TRAINER"}}}},"description":"Success"}},"summary":"Resend the email-verification code","tags":["Auth"]}},"/v1/auth/forgot-password":{"post":{"operationId":"AuthController.forgotPassword","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordResetRequestDto"},"example":{"email":"coach@example.com"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Password reset code sent","data":{"accessToken":null,"tokenType":"Bearer","expiresAt":"2026-01-15T21:30:00.000Z","status":"WAITING_FOR_CODE","message":"Password reset code sent","redirectUrl":null,"emailVerified":null,"user":null,"role":null}}}},"description":"Success"}},"summary":"Start a password reset (emails a reset code)","tags":["Auth"]}},"/v1/auth/reset-password":{"post":{"operationId":"AuthController.resetPassword","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordResetDto"},"example":{"code":"123456","email":"coach@example.com","newPassword":"N3wS3curePass!"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Password reset successfully","data":{"accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIzZjlhMWMyZS04YjdkLTRlNmYtOWEwYi0xYzJkM2U0ZjVhNmIiLCJyb2xlIjoiVFJBSU5FUiIsImlhdCI6MTc2ODQ5MjIwMH0.4y8Xq2mJ1hK9pQ7wZ3nR5tB6cV0dF1gH2jK3lM4nO5","tokenType":"Bearer","expiresAt":"2026-01-15T21:30:00.000Z","status":"SUCCESS","message":"Password reset successfully","redirectUrl":null,"emailVerified":true,"user":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","email":"coach@example.com","firstName":"Jordan","lastName":"Rivera","role":"TRAINER","isActive":true,"emailVerified":true,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"},"role":"TRAINER"}}}},"description":"Success"}},"summary":"Complete a password reset with the emailed code","tags":["Auth"]}},"/v1/auth/google/url":{"get":{"operationId":"AuthController.googleUrl","parameters":[{"in":"query","name":"redirectOnSuccess","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Google authorization URL generated successfully","data":{"authorizationUrl":"https://accounts.google.com/o/oauth2/v2/auth?client_id=123.apps.googleusercontent.com&redirect_uri=https%3A%2F%2Fapp.protocolcrm.com%2Fauth%2Fgoogle%2Fcallback&response_type=code&scope=openid%20email%20profile"}}}},"description":"Success"}},"summary":"Generate the Google OAuth authorization URL","tags":["Auth"]}},"/v1/auth/google/callback":{"post":{"operationId":"AuthController.googleCallback","requestBody":{"content":{"application/json":{"schema":{},"example":{"code":"4/0AeanS0b8xqkY7Zt3vN9pQwErTyUiOpAsDfGhJkLmNbVcXz"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Google authentication successful","data":{"accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIzZjlhMWMyZS04YjdkLTRlNmYtOWEwYi0xYzJkM2U0ZjVhNmIiLCJyb2xlIjoiVFJBSU5FUiIsImlhdCI6MTc2ODQ5MjIwMH0.4y8Xq2mJ1hK9pQ7wZ3nR5tB6cV0dF1gH2jK3lM4nO5","tokenType":"Bearer","expiresAt":"2026-01-15T21:30:00.000Z","status":"SUCCESS","message":"User authenticated successfully","redirectUrl":null,"emailVerified":true,"user":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","email":"coach@example.com","firstName":"Jordan","lastName":"Rivera","role":"TRAINER","isActive":true,"emailVerified":true,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"},"role":"TRAINER"}}}},"description":"Success"}},"summary":"Exchange a Google OAuth code for an app session token","tags":["Auth"]}},"/v1/auth/instagram/url":{"get":{"operationId":"AuthController.instagramUrl","responses":{"200":{"content":{"application/json":{}},"description":"Success"}},"summary":"Instagram auth URL — not implemented (501)","tags":["Auth"]}},"/v1/auth/instagram/callback":{"post":{"operationId":"AuthController.instagramCallback","responses":{"200":{"content":{"application/json":{}},"description":"Success"}},"summary":"Instagram auth callback — not implemented (501)","tags":["Auth"]}},"/v1/auth/tiktok/url":{"get":{"operationId":"AuthController.tiktokUrl","responses":{"200":{"content":{"application/json":{}},"description":"Success"}},"summary":"TikTok auth URL — not implemented (501)","tags":["Auth"]}},"/v1/auth/tiktok/callback":{"post":{"operationId":"AuthController.tiktokCallback","responses":{"200":{"content":{"application/json":{}},"description":"Success"}},"summary":"TikTok auth callback — not implemented (501)","tags":["Auth"]}},"/v1/auth/login-as":{"post":{"operationId":"AuthController.loginAs","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginAsRequestDto"},"example":{"userId":"7c2e9d10-4a5b-4c6d-8e9f-0a1b2c3d4e5f"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Login-as successful","data":{"accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIzZjlhMWMyZS04YjdkLTRlNmYtOWEwYi0xYzJkM2U0ZjVhNmIiLCJyb2xlIjoiVFJBSU5FUiIsImlhdCI6MTc2ODQ5MjIwMH0.4y8Xq2mJ1hK9pQ7wZ3nR5tB6cV0dF1gH2jK3lM4nO5","tokenType":"Bearer","expiresAt":"2026-01-15T21:30:00.000Z","status":"SUCCESS","message":"Login-as successful","redirectUrl":null,"emailVerified":true,"user":{"id":"7c2e9d10-4a5b-4c6d-8e9f-0a1b2c3d4e5f","email":"client@example.com","firstName":"Jordan","lastName":"Rivera","role":"CLIENT","isActive":true,"emailVerified":true,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"},"role":"CLIENT"}}}},"description":"Success"}},"summary":"Impersonate another user (admin/owner) — issues a token for them","tags":["Auth"]}},"/v1/auth/generate-client-code":{"post":{"operationId":"AuthController.generateClientCode","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateClientCodeRequestDto"},"example":{"clientId":"7c2e9d10-4a5b-4c6d-8e9f-0a1b2c3d4e5f","durationHours":24}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ClientAuthCodeResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Client login code generated","data":{"code":"482913","expiresAt":"2026-01-16T09:30:00.000Z","clientId":"7c2e9d10-4a5b-4c6d-8e9f-0a1b2c3d4e5f"}}}},"description":"Success"}},"summary":"Generate a short-lived login code for a client","tags":["Auth"]}},"/v1/auth/apiKey/":{"post":{"operationId":"ApiKeyController.create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyUpsertDto"},"example":{"name":"Zapier integration"}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ApiKeyDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"ApiKey created successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","name":"Zapier integration","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","apiKeyTemp":"EXAMPLE-api-key-shown-once-do-not-use"}}}},"description":"Tenant-scoped. The raw key is returned once as `apiKeyTemp`; store it immediately."}},"summary":"Create an API key","tags":["Api Key"],"description":"Tenant-scoped. The raw key is returned once as `apiKeyTemp`; store it immediately."},"get":{"operationId":"ApiKeyController.list","parameters":[{"in":"query","name":"page","required":false,"schema":{"type":"number"}},{"in":"query","name":"pageSize","required":false,"schema":{"type":"number"}},{"in":"query","name":"sortBy","required":false,"schema":{"type":"string"}},{"in":"query","name":"sortDir","required":false,"schema":{"type":"string"}},{"in":"query","name":"searchTerm","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"ApiKeys retrieved successfully","data":{"items":[{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","name":"Zapier integration","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z"}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Tenant-scoped page of API keys. Supports page, pageSize, sortBy, sortDir (ASC|DESC) and searchTerm (matches name)."}},"summary":"List API keys (paginated)","tags":["Api Key"],"description":"Tenant-scoped page of API keys. Supports page, pageSize, sortBy, sortDir (ASC|DESC) and searchTerm (matches name)."}},"/v1/auth/apiKey/{apiKeyId}":{"get":{"operationId":"ApiKeyController.getOne","parameters":[{"in":"path","name":"apiKeyId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"ApiKey retrieved successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","name":"Zapier integration","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z"}}}},"description":"Returns the safe view (never the raw key). 404 if not found in the caller tenant."}},"summary":"Get an API key by id","tags":["Api Key"],"description":"Returns the safe view (never the raw key). 404 if not found in the caller tenant."},"put":{"operationId":"ApiKeyController.update","parameters":[{"in":"path","name":"apiKeyId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyUpsertDto"},"example":{"name":"Zapier integration"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ApiKeyDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"ApiKey updated successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","name":"Zapier integration","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z"}}}},"description":"Only `name` is persisted. The raw key is never re-issued."}},"summary":"Update an API key","tags":["Api Key"],"description":"Only `name` is persisted. The raw key is never re-issued."},"delete":{"operationId":"ApiKeyController.remove","parameters":[{"in":"path","name":"apiKeyId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"ApiKey deleted successfully","data":null}}},"description":"Permanently removes the key. 404 if not found in the caller tenant."}},"summary":"Delete an API key","tags":["Api Key"],"description":"Permanently removes the key. 404 if not found in the caller tenant."}},"/v1/agent-connections/":{"post":{"operationId":"AgentConnectionController.create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAgentConnectionDto"},"example":{"label":"CLI agent","tier":"write"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/CreatedAgentConnectionDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Agent connection created successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","label":"CLI agent","tier":"write","lastUsedAt":null,"revokedAt":null,"createdAt":"2026-06-20T09:00:00.000Z","rawKey":"pk_live_EXAMPLE_do_not_use"}}}},"description":"Mints a per-coach MCP agent key at the given scope tier ('read' | 'write' | 'send', default 'write'). The raw key is returned exactly once — store it immediately."}},"summary":"Create an agent connection key","tags":["Agent Connection"],"description":"Mints a per-coach MCP agent key at the given scope tier ('read' | 'write' | 'send', default 'write'). The raw key is returned exactly once — store it immediately."},"get":{"operationId":"AgentConnectionController.list","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/AgentConnectionDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Agent connections retrieved successfully","data":[{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","label":"CLI agent","tier":"write","lastUsedAt":"2026-06-24T14:12:00.000Z","revokedAt":null,"createdAt":"2026-06-20T09:00:00.000Z"}]}}},"description":"Newest first. Never includes the raw key or its hash."}},"summary":"List the calling coach agent connections","tags":["Agent Connection"],"description":"Newest first. Never includes the raw key or its hash."}},"/v1/agent-connections/{id}":{"delete":{"operationId":"AgentConnectionController.revoke","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Agent connection revoked successfully","data":null}}},"description":"Revokes a connection owned by the caller so its key no longer authenticates. No-op if not found or not owned."}},"summary":"Revoke an agent connection","tags":["Agent Connection"],"description":"Revokes a connection owned by the caller so its key no longer authenticates. No-op if not found or not owned."}},"/v1/util/generate-image":{"post":{"operationId":"UtilController.generateImage","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateImageRequestDto"},"example":{"prompt":"A minimalist illustration of a kettlebell on a gym floor"}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/MediaDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Image generated successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","name":"generated-image.png","type":"IMAGE","url":"https://media.protocolcrm.com/gen/3f9a1c2e.png","thumbnailUrl":"https://media.protocolcrm.com/gen/3f9a1c2e-thumb.png","fileType":"image/png","createdAt":"2026-06-24T14:12:00.000Z","updatedAt":"2026-06-24T14:12:00.000Z"}}}},"description":"Calls the OpenAI image endpoint and uploads the result via the media pipeline. Returns the created media."}},"summary":"Generate an image from a prompt","tags":["Util"],"description":"Calls the OpenAI image endpoint and uploads the result via the media pipeline. Returns the created media."}},"/v1/util/generate-markdown":{"post":{"operationId":"UtilController.generateMarkdown","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateMarkdownRequestDto"},"example":{"instruction":"Write a short welcome message for a new client onboarding.","effort":null}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/GenerateMarkdownResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Markdown generated successfully","data":{"markdown":"# Welcome\n\nGreat to have you on board!"}}}},"description":"Success"}},"summary":"Generate markdown from an instruction","tags":["Util"]}},"/v1/util/generate-json":{"post":{"operationId":"UtilController.generateJson","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateJsonRequestDto"},"example":{"instruction":"Extract the macro targets from this plan.","responseFormat":{"protein":"number","carbs":"number","fat":"number"},"effort":null}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/GenerateJsonResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"JSON generated successfully","data":{"data":{"protein":180,"carbs":220,"fat":60}}}}},"description":"The model output is coerced to the supplied `responseFormat` shape."}},"summary":"Generate structured JSON from an instruction","tags":["Util"],"description":"The model output is coerced to the supplied `responseFormat` shape."}},"/v1/util/translations":{"post":{"operationId":"UtilController.translate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TranslationRequestDto"},"example":{"texts":[{"key":"title","value":"Hello"},{"key":"body","value":"World"}],"targetLocale":"de","sourceLocale":"en"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TranslationResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Translation completed successfully","data":{"targetLocale":"de","translations":[{"key":"title","originalValue":"Hello","translatedValue":"Hallo"},{"key":"body","originalValue":"World","translatedValue":"Welt"}]}}}},"description":"ADMIN/TRAINER only. Translates each text to `targetLocale`, preserving request key order."}},"summary":"Translate a set of keyed texts","tags":["Util"],"description":"ADMIN/TRAINER only. Translates each text to `targetLocale`, preserving request key order."}},"/v1/util/ai-usage":{"get":{"operationId":"UtilController.getAiUsage","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/AiUsageDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"AI usage retrieved successfully","data":{"aiCreditsUsed":1240}}}},"description":"Success"}},"summary":"Get AI credit usage for the caller","tags":["Util"]}},"/v1/util/configs":{"get":{"operationId":"UtilController.listConfigs","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/ConfigDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Configs retrieved successfully","data":[{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","key":"dashboard.layout","data":{"columns":3,"compact":true},"tags":["ui"],"userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z"}]}}},"description":"Success"}},"summary":"List configs visible to the caller","tags":["Util"]},"post":{"operationId":"UtilController.upsertConfig","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpsertConfigRequestDto"},"example":{"key":"dashboard.layout","data":{"columns":3,"compact":true},"tags":["ui"],"tenantLevel":false}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ConfigDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Config saved successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","key":"dashboard.layout","data":{"columns":3,"compact":true},"tags":["ui"],"userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z"}}}},"description":"ADMIN/TRAINER only. Upserts by key at either user or tenant level (`tenantLevel`)."}},"summary":"Create or update a config","tags":["Util"],"description":"ADMIN/TRAINER only. Upserts by key at either user or tenant level (`tenantLevel`)."}},"/v1/util/configs/{key}":{"delete":{"operationId":"UtilController.deleteConfig","parameters":[{"in":"path","name":"key","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"query","name":"tenantLevel","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Config deleted successfully","data":null}}},"description":"ADMIN/TRAINER only. Pass `tenantLevel=true` to delete the tenant-level config instead of the user-level one."}},"summary":"Delete a config by key","tags":["Util"],"description":"ADMIN/TRAINER only. Pass `tenantLevel=true` to delete the tenant-level config instead of the user-level one."}},"/v1/util/feedback":{"post":{"operationId":"UtilController.submitFeedback","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitFeedbackRequestDto"},"example":{"subject":"Bug in progress chart","message":"The weight chart does not render on mobile.","context":{"screen":"progress"}}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/FeedbackDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Feedback submitted successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","subject":"Bug in progress chart","message":"The weight chart does not render on mobile.","context":{"screen":"progress"},"userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","createdAt":"2026-06-24T14:12:00.000Z"}}}},"description":"Success"}},"summary":"Submit product feedback","tags":["Util"]}},"/v1/users/me":{"get":{"operationId":"UserController.getMe","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/UserDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Current user retrieved successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","email":"client@example.com","firstName":"Alex","lastName":"Kim","role":"CLIENT","phoneNumber":"+14155550123","isActive":true,"emailVerified":false,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"}}}},"description":"Success"}},"summary":"Get the current authenticated user","tags":["User"]},"put":{"operationId":"UserController.updateMe","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserUpdateDto"},"example":{"firstName":"Alex","lastName":"Kim","phoneNumber":"+14155550123","isActive":true}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/UserDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Current user updated successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","email":"client@example.com","firstName":"Alex","lastName":"Kim","role":"CLIENT","phoneNumber":"+14155550123","isActive":true,"emailVerified":false,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"}}}},"description":"Success"}},"summary":"Update the current authenticated user","tags":["User"]}},"/v1/users/":{"get":{"operationId":"UserController.list","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Users retrieved successfully","data":{"items":[{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","email":"client@example.com","firstName":"Alex","lastName":"Kim","role":"CLIENT","phoneNumber":"+14155550123","isActive":true,"emailVerified":false,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List users (paginated; filter by role, search, trainerId, etc.)","tags":["User"]},"post":{"operationId":"UserController.create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserCreateRequestDto"},"example":{"email":"client@example.com","firstName":"Alex","lastName":"Kim","phoneNumber":"+14155550123","role":"CLIENT","sendAppAccessInstructions":true,"dataCollectionMethod":"ASK_CLIENT","onboardingFormId":"5b1d8e2a-3c4f-4a6b-9c8d-7e6f5a4b3c2d"}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/UserDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"User created successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","email":"client@example.com","firstName":"Alex","lastName":"Kim","role":"CLIENT","phoneNumber":"+14155550123","isActive":true,"emailVerified":false,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"}}}},"description":"Success"}},"summary":"Create a user (defaults role to CLIENT, links to caller)","tags":["User"]}},"/v1/users/export.csv":{"get":{"operationId":"UserController.exportCsv","responses":{"200":{"content":{"application/json":{}},"description":"Returns a text/csv attachment stream, not a JSON envelope."}},"summary":"Stream a CSV export of users (select fields via repeated ?columns=)","tags":["User"],"description":"Returns a text/csv attachment stream, not a JSON envelope."}},"/v1/users/autoOnboarding":{"get":{"operationId":"UserController.latestAutoOnboardingTask","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"No auto-onboarding task in progress","data":null}}},"description":"Success"}},"summary":"Latest auto-onboarding task (registered-but-dormant; always data:null)","tags":["User"]}},"/v1/users/auto-onboarding-queue":{"get":{"operationId":"UserController.autoOnboardingQueue","parameters":[{"in":"query","name":"page","required":false,"schema":{"type":"number"}},{"in":"query","name":"pageSize","required":false,"schema":{"type":"number"}},{"in":"query","name":"status","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Auto-onboarding queue retrieved","data":{"items":[{"taskId":"9a8b7c6d-5e4f-4a3b-2c1d-0e9f8a7b6c5d","userId":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","userName":"Alex Kim","userEmail":"client@example.com","status":"PENDING","createdAt":"2026-01-15T09:30:00.000Z"}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List the auto-onboarding queue (paginated; ADMIN/TRAINER only)","tags":["User"]}},"/v1/users/{userId}":{"get":{"operationId":"UserController.getOne","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/UserDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"User retrieved successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","email":"client@example.com","firstName":"Alex","lastName":"Kim","role":"CLIENT","phoneNumber":"+14155550123","isActive":true,"emailVerified":false,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"}}}},"description":"Success"}},"summary":"Get a single user by id","tags":["User"]},"put":{"operationId":"UserController.update","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserUpdateDto"},"example":{"firstName":"Alex","lastName":"Kim","phoneNumber":"+14155550123","isActive":true}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/UserDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"User updated successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","email":"client@example.com","firstName":"Alex","lastName":"Kim","role":"CLIENT","phoneNumber":"+14155550123","isActive":true,"emailVerified":false,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"}}}},"description":"Success"}},"summary":"Update a user by id","tags":["User"]},"delete":{"operationId":"UserController.softDelete","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"User deactivated successfully","data":null}}},"description":"Success"}},"summary":"Soft-delete (deactivate) a user","tags":["User"]}},"/v1/users/{userId}/summarize":{"get":{"operationId":"UserController.summarize","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/UserSummaryDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"User summarized successfully","data":{"healthProfile":{"gender":"MALE","heightCm":180,"medicalConditions":[],"assessmentResults":[]},"fitnessProfile":{"experienceLevel":"INTERMEDIATE","primaryGoals":["STRENGTH"]},"nutritionProfile":{"primaryDietType":"KETO","nutritionGoals":["WEIGHT_LOSS"]},"behavioralProfile":{"preferredCommunicationFrequency":"WEEKLY","adherenceRate":80},"latestProgress":null}}}},"description":"Success"}},"summary":"Aggregate a user's profiles + latest progress (no LLM)","tags":["User"]}},"/v1/users/{userId}/hard":{"delete":{"operationId":"UserController.hardDelete","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"User deleted successfully","data":null}}},"description":"Success"}},"summary":"Hard-delete a user and its FK-bound child rows","tags":["User"]}},"/v1/users/{userId}/re-onboard":{"post":{"operationId":"UserController.reOnboard","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserReOnboardRequestDto"},"example":{"dataCollectionMethod":"ASK_CLIENT","onboardingFormId":"5b1d8e2a-3c4f-4a6b-9c8d-7e6f5a4b3c2d","sendAppAccessInstructions":true}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/UserDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"User re-onboarded successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","email":"client@example.com","firstName":"Alex","lastName":"Kim","role":"CLIENT","phoneNumber":"+14155550123","isActive":true,"emailVerified":false,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"}}}},"description":"Success"}},"summary":"Re-issue onboarding for an existing CLIENT","tags":["User"]}},"/v1/users/{userId}/requestDeletion":{"post":{"operationId":"UserController.requestDeletion","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Deletion request sent to your trainer","data":null}}},"description":"Success"}},"summary":"CLIENT self-service: request account deletion (emails their trainer)","tags":["User"]}},"/v1/progress-entries/":{"post":{"operationId":"ProgressEntryController.create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProgressEntryDto"},"example":{"userId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","entryDate":"2026-06-30","trainerNotes":"Great progress this month — keep the protein intake consistent.","measurements":{"weightKg":82,"bodyFatPercentage":18,"waistCm":84},"formName":"Monthly check-in"}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProgressEntryDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Progress entry created successfully","data":{"id":"7c1e2d3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f","trainerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","userId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","entryDate":"2026-06-30","trainerNotes":"Great progress this month — keep the protein intake consistent.","userNotes":"Feeling stronger, sleeping better.","measurements":{"weightKg":82,"bodyFatPercentage":18,"waistCm":84},"formName":"Monthly check-in","createdAt":"2026-06-30T08:15:00.000Z","updatedAt":"2026-06-30T08:15:00.000Z","status":"COMPLETED","assigneeIds":["a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"],"source":"COACH"}}}},"description":"Success"}},"summary":"Create a progress entry","tags":["Progress Entry"]},"get":{"operationId":"ProgressEntryController.list","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Progress entries retrieved successfully","data":{"items":[{"id":"7c1e2d3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f","trainerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","userId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","entryDate":"2026-06-30","trainerNotes":"Great progress this month — keep the protein intake consistent.","userNotes":"Feeling stronger, sleeping better.","measurements":{"weightKg":82,"bodyFatPercentage":18,"waistCm":84},"formName":"Monthly check-in","createdAt":"2026-06-30T08:15:00.000Z","updatedAt":"2026-06-30T08:15:00.000Z","status":"COMPLETED","assigneeIds":["a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"],"source":"COACH"}],"total":1,"page":1,"pageSize":10,"totalPages":1}}}},"description":"Success"}},"summary":"List progress entries (paginated, filterable)","tags":["Progress Entry"]}},"/v1/progress-entries/from-message":{"post":{"operationId":"ProgressEntryController.fromMessage","requestBody":{"content":{"application/json":{"schema":{},"example":{"messageId":"d4e5f6a7-8b9c-0d1e-2f3a-4b5c6d7e8f90","mediaId":"e5f6a7b8-9c0d-1e2f-3a4b-5c6d7e8f9a01","targetEntryId":"7c1e2d3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f"}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProgressEntryDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Message added to report","data":{"id":"7c1e2d3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f","trainerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","userId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","entryDate":"2026-06-30","trainerNotes":"Great progress this month — keep the protein intake consistent.","userNotes":"Feeling stronger, sleeping better.","measurements":{"weightKg":82,"bodyFatPercentage":18,"waistCm":84},"formName":"Monthly check-in","createdAt":"2026-06-30T08:15:00.000Z","updatedAt":"2026-06-30T08:15:00.000Z","status":"COMPLETED","assigneeIds":["a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"],"source":"COACH"}}}},"description":"Success"}},"summary":"Create a progress entry from a chat message (optionally attaching media)","tags":["Progress Entry"]}},"/v1/progress-entries/{progressEntryId}":{"get":{"operationId":"ProgressEntryController.getOne","parameters":[{"in":"path","name":"progressEntryId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Progress entry retrieved successfully","data":{"id":"7c1e2d3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f","trainerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","userId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","entryDate":"2026-06-30","trainerNotes":"Great progress this month — keep the protein intake consistent.","userNotes":"Feeling stronger, sleeping better.","measurements":{"weightKg":82,"bodyFatPercentage":18,"waistCm":84},"formName":"Monthly check-in","createdAt":"2026-06-30T08:15:00.000Z","updatedAt":"2026-06-30T08:15:00.000Z","status":"COMPLETED","assigneeIds":["a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"],"source":"COACH"}}}},"description":"Success"}},"summary":"Get a single progress entry by id (404 with body when not found)","tags":["Progress Entry"]},"put":{"operationId":"ProgressEntryController.update","parameters":[{"in":"path","name":"progressEntryId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProgressEntryDto"},"example":{"userId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","entryDate":"2026-06-30","trainerNotes":"Great progress this month — keep the protein intake consistent.","measurements":{"weightKg":82,"bodyFatPercentage":18,"waistCm":84},"formName":"Monthly check-in"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProgressEntryDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Progress entry updated successfully","data":{"id":"7c1e2d3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f","trainerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","userId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","entryDate":"2026-06-30","trainerNotes":"Great progress this month — keep the protein intake consistent.","userNotes":"Feeling stronger, sleeping better.","measurements":{"weightKg":82,"bodyFatPercentage":18,"waistCm":84},"formName":"Monthly check-in","createdAt":"2026-06-30T08:15:00.000Z","updatedAt":"2026-06-30T08:15:00.000Z","status":"COMPLETED","assigneeIds":["a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"],"source":"COACH"}}}},"description":"Success"}},"summary":"Update a progress entry","tags":["Progress Entry"]},"delete":{"operationId":"ProgressEntryController.remove","parameters":[{"in":"path","name":"progressEntryId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Progress entry deleted successfully","data":true}}},"description":"Success"}},"summary":"Delete a progress entry","tags":["Progress Entry"]}},"/v1/progress-entries/{progressEntryId}/complete":{"post":{"operationId":"ProgressEntryController.complete","parameters":[{"in":"path","name":"progressEntryId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Progress entry completed","data":{"id":"7c1e2d3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f","trainerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","userId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","entryDate":"2026-06-30","trainerNotes":"Great progress this month — keep the protein intake consistent.","userNotes":"Feeling stronger, sleeping better.","measurements":{"weightKg":82,"bodyFatPercentage":18,"waistCm":84},"formName":"Monthly check-in","createdAt":"2026-06-30T08:15:00.000Z","updatedAt":"2026-06-30T08:15:00.000Z","status":"COMPLETED","assigneeIds":["a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"],"source":"COACH"}}}},"description":"Success"}},"summary":"Mark a progress entry as completed","tags":["Progress Entry"]}},"/v1/progress-entry-reports/":{"get":{"operationId":"ProgressEntryReportController.list","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Reports fetched","data":{"items":[{"id":"9d3f1a2b-6c8e-4f0a-8b1c-2d3e4f5a6b7c","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","progressEntryId":"c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f","automationId":"f1e2d3c4-b5a6-4978-8b9c-0d1e2f3a4b5c","status":"APPROVED","priority":"STANDARD","clientFacingSummary":"Great week — down 0.8 kg and sleeping longer.","internalNotes":"Watch recovery; nudge protein to 160 g.","sections":{"summary":"Down 0.8 kg","nutrition":"Protein trending up"},"gatheredDataSnapshot":{"weightKg":81.5,"avgSleepMin":468},"dispatchedToClient":true,"createdAt":"2026-07-03T09:15:00.000Z","updatedAt":"2026-07-03T10:02:00.000Z","approvedAt":"2026-07-03T10:02:00.000Z","approvedByUserId":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","clientNotificationId":"e7f8a9b0-c1d2-4e3f-8a5b-6c7d8e9f0a1b"},{"id":"9d3f1a2b-6c8e-4f0a-8b1c-2d3e4f5a6b7c","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","progressEntryId":"c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f","automationId":"f1e2d3c4-b5a6-4978-8b9c-0d1e2f3a4b5c","status":"DRAFT","priority":"STANDARD","clientFacingSummary":"Great week — down 0.8 kg and sleeping longer.","internalNotes":"Watch recovery; nudge protein to 160 g.","sections":{"summary":"Down 0.8 kg","nutrition":"Protein trending up"},"gatheredDataSnapshot":{"weightKg":81.5,"avgSleepMin":468},"dispatchedToClient":false,"createdAt":"2026-07-03T09:15:00.000Z","updatedAt":"2026-07-03T09:15:00.000Z"}],"total":2,"page":0,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List progress-entry reports (0-indexed paginated; CLIENT scoped to own APPROVED)","tags":["Progress Entry Report"]},"post":{"operationId":"ProgressEntryReportController.create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProgressEntryReportCreateRequest"},"example":{"progressEntryId":"c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f","clientFacingSummary":"Great week — down 0.8 kg and sleeping longer.","internalNotes":"Watch recovery; nudge protein to 160 g.","priority":"STANDARD"}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProgressEntryReportDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Report created","data":{"id":"9d3f1a2b-6c8e-4f0a-8b1c-2d3e4f5a6b7c","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","progressEntryId":"c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f","automationId":"f1e2d3c4-b5a6-4978-8b9c-0d1e2f3a4b5c","status":"DRAFT","priority":"STANDARD","clientFacingSummary":"Great week — down 0.8 kg and sleeping longer.","internalNotes":"Watch recovery; nudge protein to 160 g.","sections":{"summary":"Down 0.8 kg","nutrition":"Protein trending up"},"gatheredDataSnapshot":{"weightKg":81.5,"avgSleepMin":468},"dispatchedToClient":false,"createdAt":"2026-07-03T09:15:00.000Z","updatedAt":"2026-07-03T09:15:00.000Z"}}}},"description":"Success"}},"summary":"Create a draft report (TRAINER only)","tags":["Progress Entry Report"]}},"/v1/progress-entry-reports/{id}":{"get":{"operationId":"ProgressEntryReportController.get","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProgressEntryReportDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Report fetched","data":{"id":"9d3f1a2b-6c8e-4f0a-8b1c-2d3e4f5a6b7c","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","progressEntryId":"c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f","automationId":"f1e2d3c4-b5a6-4978-8b9c-0d1e2f3a4b5c","status":"APPROVED","priority":"STANDARD","clientFacingSummary":"Great week — down 0.8 kg and sleeping longer.","internalNotes":"Watch recovery; nudge protein to 160 g.","sections":{"summary":"Down 0.8 kg","nutrition":"Protein trending up"},"gatheredDataSnapshot":{"weightKg":81.5,"avgSleepMin":468},"dispatchedToClient":true,"createdAt":"2026-07-03T09:15:00.000Z","updatedAt":"2026-07-03T10:02:00.000Z","approvedAt":"2026-07-03T10:02:00.000Z","approvedByUserId":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","clientNotificationId":"e7f8a9b0-c1d2-4e3f-8a5b-6c7d8e9f0a1b"}}}},"description":"Success"}},"summary":"Get a report by id (TRAINER/ADMIN full DTO; CLIENT client-safe projection)","tags":["Progress Entry Report"]},"put":{"operationId":"ProgressEntryReportController.updateDraft","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProgressEntryReportUpdateRequest"},"example":{"clientFacingSummary":"Great week — down 0.8 kg and sleeping longer.","priority":"URGENT","sections":{"summary":"Down 0.8 kg","nutrition":"Protein trending up"}}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProgressEntryReportDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Report updated","data":{"id":"9d3f1a2b-6c8e-4f0a-8b1c-2d3e4f5a6b7c","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","progressEntryId":"c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f","automationId":"f1e2d3c4-b5a6-4978-8b9c-0d1e2f3a4b5c","status":"DRAFT","priority":"STANDARD","clientFacingSummary":"Great week — down 0.8 kg and sleeping longer.","internalNotes":"Watch recovery; nudge protein to 160 g.","sections":{"summary":"Down 0.8 kg","nutrition":"Protein trending up"},"gatheredDataSnapshot":{"weightKg":81.5,"avgSleepMin":468},"dispatchedToClient":false,"createdAt":"2026-07-03T09:15:00.000Z","updatedAt":"2026-07-03T09:15:00.000Z"}}}},"description":"Success"}},"summary":"Update a DRAFT report (TRAINER only)","tags":["Progress Entry Report"]}},"/v1/progress-entry-reports/{id}/approve":{"post":{"operationId":"ProgressEntryReportController.approve","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProgressEntryReportApproveRequest"},"example":{"notifyClient":true}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProgressEntryReportDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Report approved","data":{"id":"9d3f1a2b-6c8e-4f0a-8b1c-2d3e4f5a6b7c","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","progressEntryId":"c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f","automationId":"f1e2d3c4-b5a6-4978-8b9c-0d1e2f3a4b5c","status":"APPROVED","priority":"STANDARD","clientFacingSummary":"Great week — down 0.8 kg and sleeping longer.","internalNotes":"Watch recovery; nudge protein to 160 g.","sections":{"summary":"Down 0.8 kg","nutrition":"Protein trending up"},"gatheredDataSnapshot":{"weightKg":81.5,"avgSleepMin":468},"dispatchedToClient":true,"createdAt":"2026-07-03T09:15:00.000Z","updatedAt":"2026-07-03T10:02:00.000Z","approvedAt":"2026-07-03T10:02:00.000Z","approvedByUserId":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","clientNotificationId":"e7f8a9b0-c1d2-4e3f-8a5b-6c7d8e9f0a1b"}}}},"description":"Success"}},"summary":"Approve a DRAFT report and deliver it to the client (TRAINER only)","tags":["Progress Entry Report"]}},"/v1/progress-entry-reports/{id}/discard":{"post":{"operationId":"ProgressEntryReportController.discard","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProgressEntryReportDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Report discarded","data":{"id":"9d3f1a2b-6c8e-4f0a-8b1c-2d3e4f5a6b7c","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","progressEntryId":"c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f","automationId":"f1e2d3c4-b5a6-4978-8b9c-0d1e2f3a4b5c","status":"DISCARDED","priority":"STANDARD","clientFacingSummary":"Great week — down 0.8 kg and sleeping longer.","internalNotes":"Watch recovery; nudge protein to 160 g.","sections":{"summary":"Down 0.8 kg","nutrition":"Protein trending up"},"gatheredDataSnapshot":{"weightKg":81.5,"avgSleepMin":468},"dispatchedToClient":false,"createdAt":"2026-07-03T09:15:00.000Z","updatedAt":"2026-07-03T09:15:00.000Z"}}}},"description":"Success"}},"summary":"Discard a DRAFT report (TRAINER only)","tags":["Progress Entry Report"]}},"/v1/progress-entry-reports/{id}/open":{"post":{"operationId":"ProgressEntryReportController.open","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ClientProgressReportDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Report opened","data":{"id":"9d3f1a2b-6c8e-4f0a-8b1c-2d3e4f5a6b7c","progressEntryId":"c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f","status":"APPROVED","priority":"STANDARD","clientFacingSummary":"Great week — down 0.8 kg and sleeping longer.","sections":{"summary":"Down 0.8 kg","nutrition":"Protein trending up"},"approvedAt":"2026-07-03T10:02:00.000Z","openedAt":"2026-07-03T12:30:00.000Z","createdAt":"2026-07-03T09:15:00.000Z","updatedAt":"2026-07-03T10:02:00.000Z"}}}},"description":"Success"}},"summary":"Mark an APPROVED report as read (CLIENT only)","tags":["Progress Entry Report"]}},"/v1/progress-entry-reports/{id}/regenerate":{"post":{"operationId":"ProgressEntryReportController.regenerate","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{}},"description":"Successful response"},"202":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProgressEntryReportRegenerateResponse"}},"required":["success","message","data"]},"example":{"success":true,"message":"Regeneration dispatched","data":{"runId":"a0b1c2d3-e4f5-4a6b-8c9d-0e1f2a3b4c5d","automationId":"f1e2d3c4-b5a6-4978-8b9c-0d1e2f3a4b5c","progressEntryId":"c4d5e6f7-a8b9-4c0d-9e1f-2a3b4c5d6e7f"}}}}}},"summary":"Dispatch a regeneration run for a report (TRAINER only, 202 Accepted)","tags":["Progress Entry Report"]}},"/v1/insights/generate":{"post":{"operationId":"ClientInsightController.generate","responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/ClientInsightDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Daily insights generated successfully","data":[{"id":"9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d","userId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","user":null,"type":"RETENTION_RISK","severity":"WARNING","title":"Sarah Mitchell has missed 2 check-ins","description":"No progress entry logged in the last 14 days after a consistent streak.","recommendation":"Send a personal check-in message to re-engage before the trend continues.","priority":3,"isRead":false,"isDismissed":false,"metadata":null,"insightDate":"2026-07-04","createdAt":"2026-07-04T06:00:00.000Z","updatedAt":"2026-07-04T06:00:00.000Z"}]}}},"description":"Success"}},"summary":"Generate the daily client insights for the current coach","tags":["Client Insight"]}},"/v1/insights/":{"get":{"operationId":"ClientInsightController.getAll","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/ClientInsightDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Insights retrieved successfully","data":[{"id":"9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d","userId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","user":null,"type":"RETENTION_RISK","severity":"WARNING","title":"Sarah Mitchell has missed 2 check-ins","description":"No progress entry logged in the last 14 days after a consistent streak.","recommendation":"Send a personal check-in message to re-engage before the trend continues.","priority":3,"isRead":false,"isDismissed":false,"metadata":null,"insightDate":"2026-07-04","createdAt":"2026-07-04T06:00:00.000Z","updatedAt":"2026-07-04T06:00:00.000Z"}]}}},"description":"Success"}},"summary":"List all insights for the current coach","tags":["Client Insight"]}},"/v1/insights/client/{clientId}":{"get":{"operationId":"ClientInsightController.getForClient","parameters":[{"in":"path","name":"clientId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/ClientInsightDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Client insights retrieved successfully","data":[{"id":"9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d","userId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","user":null,"type":"RETENTION_RISK","severity":"WARNING","title":"Sarah Mitchell has missed 2 check-ins","description":"No progress entry logged in the last 14 days after a consistent streak.","recommendation":"Send a personal check-in message to re-engage before the trend continues.","priority":3,"isRead":false,"isDismissed":false,"metadata":null,"insightDate":"2026-07-04","createdAt":"2026-07-04T06:00:00.000Z","updatedAt":"2026-07-04T06:00:00.000Z"}]}}},"description":"Success"}},"summary":"List insights for a specific client","tags":["Client Insight"]}},"/v1/insights/{insightId}/dismiss":{"put":{"operationId":"ClientInsightController.dismiss","parameters":[{"in":"path","name":"insightId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ClientInsightDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Insight dismissed successfully","data":{"id":"9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d","userId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","user":null,"type":"RETENTION_RISK","severity":"WARNING","title":"Sarah Mitchell has missed 2 check-ins","description":"No progress entry logged in the last 14 days after a consistent streak.","recommendation":"Send a personal check-in message to re-engage before the trend continues.","priority":3,"isRead":false,"isDismissed":true,"metadata":null,"insightDate":"2026-07-04","createdAt":"2026-07-04T06:00:00.000Z","updatedAt":"2026-07-04T06:00:00.000Z"}}}},"description":"Success"}},"summary":"Dismiss an insight","tags":["Client Insight"]}},"/v1/insights/{insightId}/read":{"put":{"operationId":"ClientInsightController.markAsRead","parameters":[{"in":"path","name":"insightId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ClientInsightDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Insight marked as read","data":{"id":"9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d","userId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","user":null,"type":"RETENTION_RISK","severity":"WARNING","title":"Sarah Mitchell has missed 2 check-ins","description":"No progress entry logged in the last 14 days after a consistent streak.","recommendation":"Send a personal check-in message to re-engage before the trend continues.","priority":3,"isRead":true,"isDismissed":false,"metadata":null,"insightDate":"2026-07-04","createdAt":"2026-07-04T06:00:00.000Z","updatedAt":"2026-07-04T06:00:00.000Z"}}}},"description":"Success"}},"summary":"Mark an insight as read","tags":["Client Insight"]}},"/v1/habit-logs/":{"post":{"operationId":"HabitLogController.logHabit","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HabitLogRequestDto"},"example":{"habitType":"WATER_INTAKE","userId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","logDate":"2026-07-03","completed":true,"value":3,"notes":"Hit the target before lunch.","source":"MANUAL"}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/HabitLogDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Habit logged successfully","data":{"id":"2b3c4d5e-6f7a-8b9c-0d1e-2f3a4b5c6d7e","habitType":"WATER_INTAKE","userId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","logDate":"2026-07-03","completed":true,"value":3,"notes":"Hit the target before lunch.","source":"MANUAL","loggedAt":"2026-07-03T13:20:00.000Z"}}}},"description":"Success"}},"summary":"Log a habit entry","tags":["Habit Log"]},"get":{"operationId":"HabitLogController.list","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Habit logs retrieved successfully","data":{"items":[{"id":"2b3c4d5e-6f7a-8b9c-0d1e-2f3a4b5c6d7e","habitType":"WATER_INTAKE","userId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","logDate":"2026-07-03","completed":true,"value":3,"notes":"Hit the target before lunch.","source":"MANUAL","loggedAt":"2026-07-03T13:20:00.000Z"}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List habit logs (paginated, filterable)","tags":["Habit Log"]}},"/v1/habit-logs/{id}":{"get":{"operationId":"HabitLogController.getById","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Habit log retrieved successfully","data":{"id":"2b3c4d5e-6f7a-8b9c-0d1e-2f3a4b5c6d7e","habitType":"WATER_INTAKE","userId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","logDate":"2026-07-03","completed":true,"value":3,"notes":"Hit the target before lunch.","source":"MANUAL","loggedAt":"2026-07-03T13:20:00.000Z"}}}},"description":"Success"}},"summary":"Get a single habit log by id (404 with body when not found)","tags":["Habit Log"]},"put":{"operationId":"HabitLogController.update","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HabitLogRequestDto"},"example":{"habitType":"WATER_INTAKE","userId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","logDate":"2026-07-03","completed":true,"value":3,"notes":"Hit the target before lunch.","source":"MANUAL"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/HabitLogDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Habit log updated successfully","data":{"id":"2b3c4d5e-6f7a-8b9c-0d1e-2f3a4b5c6d7e","habitType":"WATER_INTAKE","userId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","logDate":"2026-07-03","completed":true,"value":3,"notes":"Hit the target before lunch.","source":"MANUAL","loggedAt":"2026-07-03T13:20:00.000Z"}}}},"description":"Success"}},"summary":"Update a habit log","tags":["Habit Log"]},"delete":{"operationId":"HabitLogController.delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Habit log deleted successfully","data":true}}},"description":"Success"}},"summary":"Delete a habit log","tags":["Habit Log"]}},"/v1/labels/":{"get":{"operationId":"LabelController.list","parameters":[{"in":"query","name":"sector","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/LabelDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Labels retrieved","data":[{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","sector":"PROGRESS_ENTRY","name":"High priority","color":"#E4572E","displayOrder":0,"active":true}]}}},"description":"Success"}},"summary":"List labels for a sector","tags":["Label"]},"put":{"operationId":"LabelController.replaceList","parameters":[{"in":"query","name":"sector","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"items":{"type":"object","$ref":"#/components/schemas/LabelUpsertDto"},"type":"array"},"example":[{"name":"High priority","color":"#E4572E","displayOrder":0,"active":true}]}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/LabelDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Labels updated","data":[{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","sector":"PROGRESS_ENTRY","name":"High priority","color":"#E4572E","displayOrder":0,"active":true}]}}},"description":"Success"}},"summary":"Replace the label list for a sector","tags":["Label"]}},"/v1/lifecycle-stages/":{"get":{"operationId":"LifecycleStageController.list","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/LifecycleStageDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Lifecycle stages retrieved","data":[{"id":"4d5e6f7a-8b9c-0d1e-2f3a-4b5c6d7e8f90","createdAt":"2026-02-10T10:00:00.000Z","updatedAt":"2026-02-10T10:00:00.000Z","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","name":"Onboarding","color":"#4C9F70","position":0,"isTerminal":false}]}}},"description":"Success"}},"summary":"List lifecycle stages","tags":["Lifecycle Stage"]},"post":{"operationId":"LifecycleStageController.create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LifecycleStageDto"},"example":{"name":"Onboarding","color":"#4C9F70","position":0,"isTerminal":false}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/LifecycleStageDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Lifecycle stage created","data":{"id":"4d5e6f7a-8b9c-0d1e-2f3a-4b5c6d7e8f90","createdAt":"2026-02-10T10:00:00.000Z","updatedAt":"2026-02-10T10:00:00.000Z","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","name":"Onboarding","color":"#4C9F70","position":0,"isTerminal":false}}}},"description":"Success"}},"summary":"Create a lifecycle stage","tags":["Lifecycle Stage"]}},"/v1/lifecycle-stages/reorder":{"put":{"operationId":"LifecycleStageController.reorder","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LifecycleStageReorderRequestDto"},"example":{"orderedIds":["4d5e6f7a-8b9c-0d1e-2f3a-4b5c6d7e8f90","5e6f7a8b-9c0d-1e2f-3a4b-5c6d7e8f9a01"]}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/LifecycleStageDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Lifecycle stages reordered","data":[{"id":"4d5e6f7a-8b9c-0d1e-2f3a-4b5c6d7e8f90","createdAt":"2026-02-10T10:00:00.000Z","updatedAt":"2026-02-10T10:00:00.000Z","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","name":"Onboarding","color":"#4C9F70","position":0,"isTerminal":false}]}}},"description":"Success"}},"summary":"Reorder lifecycle stages","tags":["Lifecycle Stage"]}},"/v1/lifecycle-stages/{stageId}":{"put":{"operationId":"LifecycleStageController.update","parameters":[{"in":"path","name":"stageId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LifecycleStageDto"},"example":{"name":"Onboarding","color":"#4C9F70","position":0,"isTerminal":false}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/LifecycleStageDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Lifecycle stage updated","data":{"id":"4d5e6f7a-8b9c-0d1e-2f3a-4b5c6d7e8f90","createdAt":"2026-02-10T10:00:00.000Z","updatedAt":"2026-02-10T10:00:00.000Z","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","name":"Onboarding","color":"#4C9F70","position":0,"isTerminal":false}}}},"description":"Success"}},"summary":"Update a lifecycle stage","tags":["Lifecycle Stage"]},"delete":{"operationId":"LifecycleStageController.delete","parameters":[{"in":"path","name":"stageId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Lifecycle stage deleted","data":null}}},"description":"Success"}},"summary":"Delete a lifecycle stage","tags":["Lifecycle Stage"]}},"/v1/meeting-transcripts/":{"post":{"operationId":"MeetingTranscriptController.create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingTranscriptRequestDto"},"example":{"participantInfos":[{"email":"alex@example.com","firstName":"Alex","lastName":"Rivera"}],"transcript":"Coach: How did the week go?\nClient: Strong — hit all four sessions.","summary":"Client completed all planned sessions; progressing well on the strength block."}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/MeetingTranscriptResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Meeting transcript created successfully","data":{"id":"a7b8c9d0-e1f2-4a6b-bc7d-8e9f0a1b2c3d","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","tenantId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","appointmentId":null,"appointment":null,"participantInfos":[{"email":"alex@example.com","firstName":"Alex","lastName":"Rivera","profilePicture":null,"userId":"b2c3d4e5-f6a7-4b2c-9d3e-4f5a6b7c8d9e","leadId":null}],"transcript":"Coach: How did the week go?\nClient: Strong — hit all four sessions.","summary":"Client completed all planned sessions; progressing well on the strength block."}}}},"description":"Success"}},"summary":"Create a meeting transcript","tags":["Meeting Transcript"]},"get":{"operationId":"MeetingTranscriptController.list","parameters":[{"in":"query","name":"page","required":false,"schema":{"type":"number"}},{"in":"query","name":"pageSize","required":false,"schema":{"type":"number"}},{"in":"query","name":"appointmentId","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Meeting transcripts retrieved successfully","data":{"items":[{"id":"a7b8c9d0-e1f2-4a6b-bc7d-8e9f0a1b2c3d","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","tenantId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","appointmentId":null,"appointment":null,"participantInfos":[{"email":"alex@example.com","firstName":"Alex","lastName":"Rivera","profilePicture":null,"userId":"b2c3d4e5-f6a7-4b2c-9d3e-4f5a6b7c8d9e","leadId":null}],"transcript":"Coach: How did the week go?\nClient: Strong — hit all four sessions.","summary":"Client completed all planned sessions; progressing well on the strength block."}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List meeting transcripts (paginated, 1-indexed, createdAt DESC)","tags":["Meeting Transcript"]}},"/v1/meeting-transcripts/appointment/{appointmentId}":{"get":{"operationId":"MeetingTranscriptController.getByAppointmentId","parameters":[{"in":"path","name":"appointmentId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Meeting transcript retrieved successfully","data":{"id":"a7b8c9d0-e1f2-4a6b-bc7d-8e9f0a1b2c3d","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","tenantId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","appointmentId":null,"appointment":null,"participantInfos":[{"email":"alex@example.com","firstName":"Alex","lastName":"Rivera","profilePicture":null,"userId":"b2c3d4e5-f6a7-4b2c-9d3e-4f5a6b7c8d9e","leadId":null}],"transcript":"Coach: How did the week go?\nClient: Strong — hit all four sessions.","summary":"Client completed all planned sessions; progressing well on the strength block."}}}},"description":"Success"}},"summary":"Get the transcript for an appointment (404 when none)","tags":["Meeting Transcript"]}},"/v1/meeting-transcripts/{transcriptId}":{"get":{"operationId":"MeetingTranscriptController.getById","parameters":[{"in":"path","name":"transcriptId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Meeting transcript retrieved successfully","data":{"id":"a7b8c9d0-e1f2-4a6b-bc7d-8e9f0a1b2c3d","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","tenantId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","appointmentId":null,"appointment":null,"participantInfos":[{"email":"alex@example.com","firstName":"Alex","lastName":"Rivera","profilePicture":null,"userId":"b2c3d4e5-f6a7-4b2c-9d3e-4f5a6b7c8d9e","leadId":null}],"transcript":"Coach: How did the week go?\nClient: Strong — hit all four sessions.","summary":"Client completed all planned sessions; progressing well on the strength block."}}}},"description":"Success"}},"summary":"Get a meeting transcript by id (404 when not found)","tags":["Meeting Transcript"]},"put":{"operationId":"MeetingTranscriptController.update","parameters":[{"in":"path","name":"transcriptId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeetingTranscriptRequestDto"},"example":{"summary":"Updated summary after review."}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/MeetingTranscriptResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Meeting transcript updated successfully","data":{"id":"a7b8c9d0-e1f2-4a6b-bc7d-8e9f0a1b2c3d","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","tenantId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","appointmentId":null,"appointment":null,"participantInfos":[{"email":"alex@example.com","firstName":"Alex","lastName":"Rivera","profilePicture":null,"userId":"b2c3d4e5-f6a7-4b2c-9d3e-4f5a6b7c8d9e","leadId":null}],"transcript":"Coach: How did the week go?\nClient: Strong — hit all four sessions.","summary":"Updated summary after review."}}}},"description":"Success"}},"summary":"Update a transcript (participantInfos / transcript / summary; appointmentId not updatable)","tags":["Meeting Transcript"]},"delete":{"operationId":"MeetingTranscriptController.delete","parameters":[{"in":"path","name":"transcriptId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Meeting transcript deleted successfully","data":"Deleted"}}},"description":"Success"}},"summary":"Delete a meeting transcript","tags":["Meeting Transcript"]}},"/v1/onboarding/":{"get":{"operationId":"OnboardingController.getOnboardingData","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/OnboardingDataDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Onboarding data retrieved successfully","data":{"businessName":"Acme Fitness","tagline":"Train hard","yearsExperience":"5","certifications":["NASM"],"specializations":["STRENGTH"],"trainingStyle":"IN_PERSON","targetDemographics":["ATHLETES"],"profilePhoto":{"id":"pp1","url":"http://x/pp.jpg"},"logo":{"id":"logo1","url":"http://x/logo.png"},"primaryColor":"#111111","secondaryColor":"#222222","primaryGoal":"GROW","monthlyClientTarget":20,"referralSource":"INSTAGRAM","country":"US","timezone":"America/New_York"}}}},"description":"Success"}},"summary":"Get the trainer onboarding data","tags":["Onboarding"]},"put":{"operationId":"OnboardingController.saveOnboardingData","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingDataDto"},"example":{"businessName":"Acme Fitness","tagline":"Train hard","yearsExperience":"5","certifications":["NASM"],"specializations":["STRENGTH"],"trainingStyle":"IN_PERSON","targetDemographics":["ATHLETES"],"profilePhoto":{"id":"pp1","url":"http://x/pp.jpg"},"logo":{"id":"logo1","url":"http://x/logo.png"},"primaryColor":"#111111","secondaryColor":"#222222","primaryGoal":"GROW","monthlyClientTarget":20,"referralSource":"INSTAGRAM","country":"US","timezone":"America/New_York"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Onboarding data saved successfully","data":{}}}},"description":"Success"}},"summary":"Save (partial) trainer onboarding data","tags":["Onboarding"]}},"/v1/onboarding/complete":{"post":{"operationId":"OnboardingController.completeOnboarding","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingDataDto"},"example":{"businessName":"Acme Fitness","tagline":"Train hard","yearsExperience":"5","certifications":["NASM"],"specializations":["STRENGTH"],"trainingStyle":"IN_PERSON","targetDemographics":["ATHLETES"],"profilePhoto":{"id":"pp1","url":"http://x/pp.jpg"},"logo":{"id":"logo1","url":"http://x/logo.png"},"primaryColor":"#111111","secondaryColor":"#222222","primaryGoal":"GROW","monthlyClientTarget":20,"referralSource":"INSTAGRAM","country":"US","timezone":"America/New_York"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/OnboardingStatusDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Onboarding completed successfully","data":{"status":"COMPLETED","completedAt":"2026-01-15T09:30:00.000Z","skippedAt":null}}}},"description":"Success"}},"summary":"Save data then mark onboarding COMPLETED","tags":["Onboarding"]}},"/v1/onboarding/skip":{"post":{"operationId":"OnboardingController.skipOnboarding","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/OnboardingStatusDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Onboarding skipped successfully","data":{"status":"SKIPPED","completedAt":null,"skippedAt":"2026-01-15T09:30:00.000Z"}}}},"description":"Success"}},"summary":"Skip onboarding (marks SKIPPED)","tags":["Onboarding"]}},"/v1/onboarding/status":{"get":{"operationId":"OnboardingController.getOnboardingStatus","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/OnboardingStatusDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Onboarding status retrieved successfully","data":{"status":"NOT_STARTED","completedAt":null,"skippedAt":null}}}},"description":"Success"}},"summary":"Get the onboarding status (defaults null status to NOT_STARTED)","tags":["Onboarding"]}},"/v1/users/{userId}/pushToken/":{"post":{"operationId":"PushTokenController.registerPushToken","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterPushTokenRequestDto"},"example":{"token":"ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]","deviceId":"device-1","platform":"IOS","deviceName":"iPhone 15"}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/PushTokenDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Push token registered successfully","data":{"id":"b7d3e2a1-9c8f-4a6b-8e2d-1f0a9b8c7d6e","token":"ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]","deviceId":"device-1","platform":"IOS","deviceName":"iPhone 15","isActive":true,"lastUsedAt":"2026-01-15T09:30:00.000Z","userId":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z"}}}},"description":"Success"}},"summary":"Register a push token for a user","tags":["Push Token"]},"delete":{"operationId":"PushTokenController.removePushToken","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"query","name":"token","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Push token removed successfully","data":null}}},"description":"Success"}},"summary":"Remove a push token (by ?token=) for a user","tags":["Push Token"]},"get":{"operationId":"PushTokenController.getPushTokens","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/PushTokenDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Push tokens retrieved successfully","data":[{"id":"b7d3e2a1-9c8f-4a6b-8e2d-1f0a9b8c7d6e","token":"ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]","deviceId":"device-1","platform":"IOS","deviceName":"iPhone 15","isActive":true,"lastUsedAt":"2026-01-15T09:30:00.000Z","userId":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z"}]}}},"description":"Success"}},"summary":"List a user's push tokens","tags":["Push Token"]}},"/v1/tasks/":{"post":{"operationId":"TaskController.createTask","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCreateDto"},"example":{"columnId":"b2f4d6a8-1c3e-4a5b-8d7f-9e0a1b2c3d4e","title":"Draft Q2 nutrition plan for Marcus","description":"Build the macro targets and meal template ahead of the check-in call.","priority":"HIGH","dueDate":"2026-03-20","clientId":"5e7a9c1b-3d5f-4a7b-9c1d-2e4f6a8b0c2d","assigneeIds":["9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8"],"labelIds":["c4e6a8b0-2d4f-4a6b-8c0d-1e3f5a7b9c1d"],"estimatedMinutes":60}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TaskDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Task created successfully","data":{"id":"7c9e6a41-2b3d-4f8a-9c1e-5d6f7a8b9c0d","createdAt":"2026-03-10T08:15:00.000Z","updatedAt":"2026-03-12T14:20:00.000Z","columnId":"b2f4d6a8-1c3e-4a5b-8d7f-9e0a1b2c3d4e","boardId":"a1c3e5f7-9b8d-4e2a-b6c4-d8f0a2b4c6d8","title":"Draft Q2 nutrition plan for Marcus","description":"Build the macro targets and meal template ahead of the check-in call.","position":1,"priority":"HIGH","dueDate":"2026-03-20","clientId":"5e7a9c1b-3d5f-4a7b-9c1d-2e4f6a8b0c2d","createdById":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","assigneeIds":["9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8"],"labelIds":["c4e6a8b0-2d4f-4a6b-8c0d-1e3f5a7b9c1d"],"subtaskProgress":{"completed":1,"total":3},"isArchived":false,"isPrivate":false,"estimatedMinutes":60,"isOverdue":false}}}},"description":"Success"}},"summary":"Create a task","tags":["Task"]},"get":{"operationId":"TaskController.listTasks","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Tasks retrieved successfully","data":{"items":[{"id":"7c9e6a41-2b3d-4f8a-9c1e-5d6f7a8b9c0d","createdAt":"2026-03-10T08:15:00.000Z","updatedAt":"2026-03-12T14:20:00.000Z","columnId":"b2f4d6a8-1c3e-4a5b-8d7f-9e0a1b2c3d4e","boardId":"a1c3e5f7-9b8d-4e2a-b6c4-d8f0a2b4c6d8","title":"Draft Q2 nutrition plan for Marcus","description":"Build the macro targets and meal template ahead of the check-in call.","position":1,"priority":"HIGH","dueDate":"2026-03-20","clientId":"5e7a9c1b-3d5f-4a7b-9c1d-2e4f6a8b0c2d","createdById":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","assigneeIds":["9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8"],"labelIds":["c4e6a8b0-2d4f-4a6b-8c0d-1e3f5a7b9c1d"],"subtaskProgress":{"completed":1,"total":3},"isArchived":false,"isPrivate":false,"estimatedMinutes":60,"isOverdue":false}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List tasks (paginated, filterable)","tags":["Task"]}},"/v1/tasks/my-tasks":{"get":{"operationId":"TaskController.getMyTasks","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/TaskDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"My tasks retrieved successfully","data":[{"id":"7c9e6a41-2b3d-4f8a-9c1e-5d6f7a8b9c0d","createdAt":"2026-03-10T08:15:00.000Z","updatedAt":"2026-03-12T14:20:00.000Z","columnId":"b2f4d6a8-1c3e-4a5b-8d7f-9e0a1b2c3d4e","boardId":"a1c3e5f7-9b8d-4e2a-b6c4-d8f0a2b4c6d8","title":"Draft Q2 nutrition plan for Marcus","description":"Build the macro targets and meal template ahead of the check-in call.","position":1,"priority":"HIGH","dueDate":"2026-03-20","clientId":"5e7a9c1b-3d5f-4a7b-9c1d-2e4f6a8b0c2d","createdById":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","assigneeIds":["9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8"],"labelIds":["c4e6a8b0-2d4f-4a6b-8c0d-1e3f5a7b9c1d"],"subtaskProgress":{"completed":1,"total":3},"isArchived":false,"isPrivate":false,"estimatedMinutes":60,"isOverdue":false}]}}},"description":"Success"}},"summary":"List tasks assigned to the current user","tags":["Task"]}},"/v1/tasks/archive-completed":{"post":{"operationId":"TaskController.archiveAllCompletedTasks","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Archived 4 completed tasks","data":{"archivedCount":4}}}},"description":"Success"}},"summary":"Archive all completed tasks","tags":["Task"]}},"/v1/tasks/{taskId}":{"get":{"operationId":"TaskController.getTaskById","parameters":[{"in":"path","name":"taskId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TaskDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Task retrieved successfully","data":{"id":"7c9e6a41-2b3d-4f8a-9c1e-5d6f7a8b9c0d","createdAt":"2026-03-10T08:15:00.000Z","updatedAt":"2026-03-12T14:20:00.000Z","columnId":"b2f4d6a8-1c3e-4a5b-8d7f-9e0a1b2c3d4e","boardId":"a1c3e5f7-9b8d-4e2a-b6c4-d8f0a2b4c6d8","title":"Draft Q2 nutrition plan for Marcus","description":"Build the macro targets and meal template ahead of the check-in call.","position":1,"priority":"HIGH","dueDate":"2026-03-20","clientId":"5e7a9c1b-3d5f-4a7b-9c1d-2e4f6a8b0c2d","createdById":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","assigneeIds":["9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8"],"labelIds":["c4e6a8b0-2d4f-4a6b-8c0d-1e3f5a7b9c1d"],"subtaskProgress":{"completed":1,"total":3},"isArchived":false,"isPrivate":false,"estimatedMinutes":60,"isOverdue":false}}}},"description":"Success"}},"summary":"Get a task by id","tags":["Task"]},"put":{"operationId":"TaskController.updateTask","parameters":[{"in":"path","name":"taskId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskUpdateDto"},"example":{"title":"Draft Q2 nutrition plan for Marcus (revised)","priority":"MEDIUM","estimatedMinutes":90}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TaskDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Task updated successfully","data":{"id":"7c9e6a41-2b3d-4f8a-9c1e-5d6f7a8b9c0d","createdAt":"2026-03-10T08:15:00.000Z","updatedAt":"2026-03-12T14:20:00.000Z","columnId":"b2f4d6a8-1c3e-4a5b-8d7f-9e0a1b2c3d4e","boardId":"a1c3e5f7-9b8d-4e2a-b6c4-d8f0a2b4c6d8","title":"Draft Q2 nutrition plan for Marcus","description":"Build the macro targets and meal template ahead of the check-in call.","position":1,"priority":"HIGH","dueDate":"2026-03-20","clientId":"5e7a9c1b-3d5f-4a7b-9c1d-2e4f6a8b0c2d","createdById":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","assigneeIds":["9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8"],"labelIds":["c4e6a8b0-2d4f-4a6b-8c0d-1e3f5a7b9c1d"],"subtaskProgress":{"completed":1,"total":3},"isArchived":false,"isPrivate":false,"estimatedMinutes":60,"isOverdue":false}}}},"description":"Success"}},"summary":"Update a task","tags":["Task"]},"delete":{"operationId":"TaskController.deleteTask","parameters":[{"in":"path","name":"taskId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Task deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete a task","tags":["Task"]}},"/v1/tasks/{taskId}/move":{"put":{"operationId":"TaskController.moveTask","parameters":[{"in":"path","name":"taskId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskMoveDto"},"example":{"columnId":"e8a0c2d4-6f8a-4b0c-9d2e-3f5a7b9c1d3e","position":2}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TaskDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Task moved successfully","data":{"id":"7c9e6a41-2b3d-4f8a-9c1e-5d6f7a8b9c0d","createdAt":"2026-03-10T08:15:00.000Z","updatedAt":"2026-03-12T14:20:00.000Z","columnId":"b2f4d6a8-1c3e-4a5b-8d7f-9e0a1b2c3d4e","boardId":"a1c3e5f7-9b8d-4e2a-b6c4-d8f0a2b4c6d8","title":"Draft Q2 nutrition plan for Marcus","description":"Build the macro targets and meal template ahead of the check-in call.","position":1,"priority":"HIGH","dueDate":"2026-03-20","clientId":"5e7a9c1b-3d5f-4a7b-9c1d-2e4f6a8b0c2d","createdById":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","assigneeIds":["9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8"],"labelIds":["c4e6a8b0-2d4f-4a6b-8c0d-1e3f5a7b9c1d"],"subtaskProgress":{"completed":1,"total":3},"isArchived":false,"isPrivate":false,"estimatedMinutes":60,"isOverdue":false}}}},"description":"Success"}},"summary":"Move a task to another column/position","tags":["Task"]}},"/v1/tasks/{taskId}/complete":{"put":{"operationId":"TaskController.completeTask","parameters":[{"in":"path","name":"taskId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TaskDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Task completion toggled successfully","data":{"id":"7c9e6a41-2b3d-4f8a-9c1e-5d6f7a8b9c0d","createdAt":"2026-03-10T08:15:00.000Z","updatedAt":"2026-03-12T14:20:00.000Z","columnId":"b2f4d6a8-1c3e-4a5b-8d7f-9e0a1b2c3d4e","boardId":"a1c3e5f7-9b8d-4e2a-b6c4-d8f0a2b4c6d8","title":"Draft Q2 nutrition plan for Marcus","description":"Build the macro targets and meal template ahead of the check-in call.","position":1,"priority":"HIGH","dueDate":"2026-03-20","clientId":"5e7a9c1b-3d5f-4a7b-9c1d-2e4f6a8b0c2d","createdById":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","assigneeIds":["9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8"],"labelIds":["c4e6a8b0-2d4f-4a6b-8c0d-1e3f5a7b9c1d"],"subtaskProgress":{"completed":1,"total":3},"isArchived":false,"isPrivate":false,"estimatedMinutes":60,"isOverdue":false}}}},"description":"Success"}},"summary":"Toggle a task between completed and incomplete","tags":["Task"]}},"/v1/tasks/{taskId}/subtasks/reorder":{"put":{"operationId":"TaskController.reorderSubtasks","parameters":[{"in":"path","name":"taskId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubtaskReorderDto"},"example":{"subtaskIds":["d5f7a9c1-3e5a-4b7c-9d1e-2f4a6b8c0d2e","f7a9c1e3-5a7b-4c9d-1e3f-4a6b8c0d2e4f"]}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/TaskSubtaskDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Subtasks reordered successfully","data":[{"id":"d5f7a9c1-3e5a-4b7c-9d1e-2f4a6b8c0d2e","createdAt":"2026-03-10T08:20:00.000Z","updatedAt":"2026-03-11T09:00:00.000Z","taskId":"7c9e6a41-2b3d-4f8a-9c1e-5d6f7a8b9c0d","title":"Calculate protein target","position":0,"isCompleted":true,"completedAt":"2026-03-11T09:00:00.000Z","completedById":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","assigneeId":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8"}]}}},"description":"Success"}},"summary":"Reorder the subtasks of a task","tags":["Task"]}},"/v1/tasks/{taskId}/subtasks":{"post":{"operationId":"TaskController.createSubtask","parameters":[{"in":"path","name":"taskId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskSubtaskCreateDto"},"example":{"title":"Calculate protein target","position":0,"assigneeId":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8"}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TaskSubtaskDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Subtask created successfully","data":{"id":"d5f7a9c1-3e5a-4b7c-9d1e-2f4a6b8c0d2e","createdAt":"2026-03-10T08:20:00.000Z","updatedAt":"2026-03-11T09:00:00.000Z","taskId":"7c9e6a41-2b3d-4f8a-9c1e-5d6f7a8b9c0d","title":"Calculate protein target","position":0,"isCompleted":true,"completedAt":"2026-03-11T09:00:00.000Z","completedById":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","assigneeId":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8"}}}},"description":"Success"}},"summary":"Create a subtask under a task","tags":["Task"]}},"/v1/tasks/{taskId}/subtasks/{subtaskId}":{"put":{"operationId":"TaskController.updateSubtask","parameters":[{"in":"path","name":"taskId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"path","name":"subtaskId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskSubtaskUpdateDto"},"example":{"title":"Calculate protein and fat targets","isCompleted":true}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TaskSubtaskDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Subtask updated successfully","data":{"id":"d5f7a9c1-3e5a-4b7c-9d1e-2f4a6b8c0d2e","createdAt":"2026-03-10T08:20:00.000Z","updatedAt":"2026-03-11T09:00:00.000Z","taskId":"7c9e6a41-2b3d-4f8a-9c1e-5d6f7a8b9c0d","title":"Calculate protein target","position":0,"isCompleted":true,"completedAt":"2026-03-11T09:00:00.000Z","completedById":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","assigneeId":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8"}}}},"description":"Success"}},"summary":"Update a subtask","tags":["Task"]},"delete":{"operationId":"TaskController.deleteSubtask","parameters":[{"in":"path","name":"taskId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"path","name":"subtaskId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Subtask deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete a subtask","tags":["Task"]}},"/v1/tasks/{taskId}/subtasks/{subtaskId}/toggle":{"put":{"operationId":"TaskController.toggleSubtask","parameters":[{"in":"path","name":"taskId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"path","name":"subtaskId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TaskSubtaskDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Subtask toggled successfully","data":{"id":"d5f7a9c1-3e5a-4b7c-9d1e-2f4a6b8c0d2e","createdAt":"2026-03-10T08:20:00.000Z","updatedAt":"2026-03-11T09:00:00.000Z","taskId":"7c9e6a41-2b3d-4f8a-9c1e-5d6f7a8b9c0d","title":"Calculate protein target","position":0,"isCompleted":true,"completedAt":"2026-03-11T09:00:00.000Z","completedById":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","assigneeId":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8"}}}},"description":"Success"}},"summary":"Toggle a subtask between completed and incomplete","tags":["Task"]}},"/v1/boards/":{"post":{"operationId":"TaskBoardController.createBoard","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskBoardCreateDto"},"example":{"name":"Client onboarding","description":"Kanban board tracking new client onboarding steps.","color":"#3A6EA5","icon":"clipboard","defaultColumns":["To do","In progress","Done"]}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TaskBoardDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Task board created successfully","data":{"id":"a1c3e5f7-9b8d-4e2a-b6c4-d8f0a2b4c6d8","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-05T16:45:00.000Z","name":"Client onboarding","description":"Kanban board tracking new client onboarding steps.","ownerId":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","isArchived":false,"color":"#3A6EA5","icon":"clipboard","createdById":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","columns":[{"id":"c3e5a7b9-1d3f-4a5b-8c7d-9e1f3a5b7c9d","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-01T10:00:00.000Z","boardId":"a1c3e5f7-9b8d-4e2a-b6c4-d8f0a2b4c6d8","name":"In progress","position":1,"color":"#4C9F70","wipLimit":5,"isDoneColumn":false,"taskCount":3}],"taskCount":8}}}},"description":"Success"}},"summary":"Create a task board","tags":["Task Board"]},"get":{"operationId":"TaskBoardController.listBoards","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Task boards retrieved successfully","data":{"items":[{"id":"a1c3e5f7-9b8d-4e2a-b6c4-d8f0a2b4c6d8","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-05T16:45:00.000Z","name":"Client onboarding","description":"Kanban board tracking new client onboarding steps.","ownerId":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","isArchived":false,"color":"#3A6EA5","icon":"clipboard","createdById":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","columns":[{"id":"c3e5a7b9-1d3f-4a5b-8c7d-9e1f3a5b7c9d","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-01T10:00:00.000Z","boardId":"a1c3e5f7-9b8d-4e2a-b6c4-d8f0a2b4c6d8","name":"In progress","position":1,"color":"#4C9F70","wipLimit":5,"isDoneColumn":false,"taskCount":3}],"taskCount":8}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List task boards (paginated, filterable)","tags":["Task Board"]}},"/v1/boards/{boardId}":{"get":{"operationId":"TaskBoardController.getBoardById","parameters":[{"in":"path","name":"boardId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TaskBoardDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Task board retrieved successfully","data":{"id":"a1c3e5f7-9b8d-4e2a-b6c4-d8f0a2b4c6d8","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-05T16:45:00.000Z","name":"Client onboarding","description":"Kanban board tracking new client onboarding steps.","ownerId":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","isArchived":false,"color":"#3A6EA5","icon":"clipboard","createdById":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","columns":[{"id":"c3e5a7b9-1d3f-4a5b-8c7d-9e1f3a5b7c9d","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-01T10:00:00.000Z","boardId":"a1c3e5f7-9b8d-4e2a-b6c4-d8f0a2b4c6d8","name":"In progress","position":1,"color":"#4C9F70","wipLimit":5,"isDoneColumn":false,"taskCount":3}],"taskCount":8}}}},"description":"Success"}},"summary":"Get a task board by id (full=true includes columns and tasks)","tags":["Task Board"]},"put":{"operationId":"TaskBoardController.updateBoard","parameters":[{"in":"path","name":"boardId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskBoardUpdateDto"},"example":{"name":"Client onboarding (2026)","description":"Refreshed onboarding board for the 2026 cohort.","color":"#2E5E8C"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TaskBoardDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Task board updated successfully","data":{"id":"a1c3e5f7-9b8d-4e2a-b6c4-d8f0a2b4c6d8","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-05T16:45:00.000Z","name":"Client onboarding","description":"Kanban board tracking new client onboarding steps.","ownerId":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","isArchived":false,"color":"#3A6EA5","icon":"clipboard","createdById":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","columns":[{"id":"c3e5a7b9-1d3f-4a5b-8c7d-9e1f3a5b7c9d","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-01T10:00:00.000Z","boardId":"a1c3e5f7-9b8d-4e2a-b6c4-d8f0a2b4c6d8","name":"In progress","position":1,"color":"#4C9F70","wipLimit":5,"isDoneColumn":false,"taskCount":3}],"taskCount":8}}}},"description":"Success"}},"summary":"Update a task board","tags":["Task Board"]},"delete":{"operationId":"TaskBoardController.deleteBoard","parameters":[{"in":"path","name":"boardId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Task board deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete a task board","tags":["Task Board"]}},"/v1/boards/{boardId}/columns/reorder":{"put":{"operationId":"TaskBoardController.reorderColumns","parameters":[{"in":"path","name":"boardId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ColumnReorderDto"},"example":{"columnIds":["c3e5a7b9-1d3f-4a5b-8c7d-9e1f3a5b7c9d","e5a7b9c1-3f5a-4b7c-9d1e-3f5a7b9c1d3e"]}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/TaskBoardColumnDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Columns reordered successfully","data":[{"id":"c3e5a7b9-1d3f-4a5b-8c7d-9e1f3a5b7c9d","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-01T10:00:00.000Z","boardId":"a1c3e5f7-9b8d-4e2a-b6c4-d8f0a2b4c6d8","name":"In progress","position":1,"color":"#4C9F70","wipLimit":5,"isDoneColumn":false,"taskCount":3}]}}},"description":"Success"}},"summary":"Reorder the columns of a board","tags":["Task Board"]}},"/v1/boards/{boardId}/columns":{"post":{"operationId":"TaskBoardController.createColumn","parameters":[{"in":"path","name":"boardId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskBoardColumnCreateDto"},"example":{"name":"In progress","position":1,"color":"#4C9F70","wipLimit":5,"isDoneColumn":false}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TaskBoardColumnDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Column created successfully","data":{"id":"c3e5a7b9-1d3f-4a5b-8c7d-9e1f3a5b7c9d","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-01T10:00:00.000Z","boardId":"a1c3e5f7-9b8d-4e2a-b6c4-d8f0a2b4c6d8","name":"In progress","position":1,"color":"#4C9F70","wipLimit":5,"isDoneColumn":false,"taskCount":3}}}},"description":"Success"}},"summary":"Create a column on a board","tags":["Task Board"]}},"/v1/boards/{boardId}/columns/{columnId}":{"put":{"operationId":"TaskBoardController.updateColumn","parameters":[{"in":"path","name":"boardId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"path","name":"columnId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskBoardColumnUpdateDto"},"example":{"name":"In review","color":"#E0A458","wipLimit":3}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TaskBoardColumnDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Column updated successfully","data":{"id":"c3e5a7b9-1d3f-4a5b-8c7d-9e1f3a5b7c9d","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-01T10:00:00.000Z","boardId":"a1c3e5f7-9b8d-4e2a-b6c4-d8f0a2b4c6d8","name":"In progress","position":1,"color":"#4C9F70","wipLimit":5,"isDoneColumn":false,"taskCount":3}}}},"description":"Success"}},"summary":"Update a board column","tags":["Task Board"]},"delete":{"operationId":"TaskBoardController.deleteColumn","parameters":[{"in":"path","name":"boardId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"path","name":"columnId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Column deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete a board column","tags":["Task Board"]}},"/v1/task-labels/":{"post":{"operationId":"TaskLabelController.createLabel","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskLabelCreateDto"},"example":{"name":"Nutrition","color":"#4C9F70","description":"Tasks related to meal plans and macros."}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TaskLabelDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Task label created successfully","data":{"id":"b4d6f8a0-2c4e-4a6b-8d0f-1e3a5c7b9d1f","createdAt":"2026-02-14T11:00:00.000Z","updatedAt":"2026-02-14T11:00:00.000Z","name":"Nutrition","color":"#4C9F70","description":"Tasks related to meal plans and macros.","ownerId":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","taskCount":12}}}},"description":"Success"}},"summary":"Create a task label","tags":["Task Label"]},"get":{"operationId":"TaskLabelController.listLabels","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Task labels retrieved successfully","data":{"items":[{"id":"b4d6f8a0-2c4e-4a6b-8d0f-1e3a5c7b9d1f","createdAt":"2026-02-14T11:00:00.000Z","updatedAt":"2026-02-14T11:00:00.000Z","name":"Nutrition","color":"#4C9F70","description":"Tasks related to meal plans and macros.","ownerId":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","taskCount":12}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List task labels (paginated, filterable)","tags":["Task Label"]}},"/v1/task-labels/{labelId}":{"get":{"operationId":"TaskLabelController.getLabelById","parameters":[{"in":"path","name":"labelId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TaskLabelDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Task label retrieved successfully","data":{"id":"b4d6f8a0-2c4e-4a6b-8d0f-1e3a5c7b9d1f","createdAt":"2026-02-14T11:00:00.000Z","updatedAt":"2026-02-14T11:00:00.000Z","name":"Nutrition","color":"#4C9F70","description":"Tasks related to meal plans and macros.","ownerId":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","taskCount":12}}}},"description":"Success"}},"summary":"Get a task label by id","tags":["Task Label"]},"put":{"operationId":"TaskLabelController.updateLabel","parameters":[{"in":"path","name":"labelId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskLabelUpdateDto"},"example":{"name":"Nutrition & supplements","color":"#3E8E63"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TaskLabelDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Task label updated successfully","data":{"id":"b4d6f8a0-2c4e-4a6b-8d0f-1e3a5c7b9d1f","createdAt":"2026-02-14T11:00:00.000Z","updatedAt":"2026-02-14T11:00:00.000Z","name":"Nutrition","color":"#4C9F70","description":"Tasks related to meal plans and macros.","ownerId":"9f1a3b5c-7d9e-4f2a-b4c6-d8e0f2a4b6c8","taskCount":12}}}},"description":"Success"}},"summary":"Update a task label","tags":["Task Label"]},"delete":{"operationId":"TaskLabelController.deleteLabel","parameters":[{"in":"path","name":"labelId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Task label deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete a task label","tags":["Task Label"]}},"/v1/scheduling/appointments/":{"post":{"operationId":"AppointmentController.createAppointment","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentDto"},"example":{"startTime":"2026-01-20T14:00:00.000Z","endTime":"2026-01-20T15:00:00.000Z","title":"Personal Training Session","type":"PERSONAL_TRAINING","modality":"IN_PERSON","location":"Downtown Studio, Floor 2"}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/AppointmentDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Appointment created successfully","data":{"id":"7c1f2a3b-4d5e-6f70-8a91-b2c3d4e5f601","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","startTime":"2026-01-20T14:00:00.000Z","endTime":"2026-01-20T15:00:00.000Z","title":"Personal Training Session","description":"Lower-body strength + mobility.","type":"PERSONAL_TRAINING","status":"SCHEDULED","modality":"IN_PERSON","location":"Downtown Studio, Floor 2","isRecurring":false,"reminderSent":false,"checkInRequired":false,"trainer":{"id":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","firstName":"Dijana","lastName":"Keri","email":"dijana@protocolcrm.com"},"participantInfos":[{"userId":"2b3c4d5e-6f70-8a91-b2c3-d4e5f6071829","firstName":"Marko","lastName":"Petrovic","email":"marko@example.com"}],"tenantId":"f0e1d2c3-b4a5-6879-8a9b-0c1d2e3f4a5b"}}}},"description":"Success"}},"summary":"Create an appointment","tags":["Appointment"]},"get":{"operationId":"AppointmentController.listAppointments","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Appointments retrieved successfully","data":{"items":[{"id":"7c1f2a3b-4d5e-6f70-8a91-b2c3d4e5f601","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","startTime":"2026-01-20T14:00:00.000Z","endTime":"2026-01-20T15:00:00.000Z","title":"Personal Training Session","description":"Lower-body strength + mobility.","type":"PERSONAL_TRAINING","status":"SCHEDULED","modality":"IN_PERSON","location":"Downtown Studio, Floor 2","isRecurring":false,"reminderSent":false,"checkInRequired":false,"trainer":{"id":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","firstName":"Dijana","lastName":"Keri","email":"dijana@protocolcrm.com"},"participantInfos":[{"userId":"2b3c4d5e-6f70-8a91-b2c3-d4e5f6071829","firstName":"Marko","lastName":"Petrovic","email":"marko@example.com"}],"tenantId":"f0e1d2c3-b4a5-6879-8a9b-0c1d2e3f4a5b"}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List appointments (1-indexed, paginated; expands recurring series)","tags":["Appointment"]}},"/v1/scheduling/appointments/analytics":{"get":{"operationId":"AppointmentController.getAppointmentAnalytics","parameters":[{"in":"query","name":"startDate","required":false,"schema":{"type":"string"}},{"in":"query","name":"endDate","required":false,"schema":{"type":"string"}},{"in":"query","name":"participantId","required":false,"schema":{"type":"string"}},{"in":"query","name":"trainerId","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/AppointmentAnalyticsDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Appointment analytics retrieved successfully","data":{"totalAppointments":42,"completedAppointments":30,"cancelledAppointments":6,"noShowAppointments":3,"attendanceRate":0.83,"averageSessionDuration":55,"mostPopularDays":{"MONDAY":12,"WEDNESDAY":10,"FRIDAY":8},"mostPopularTimes":{"09:00":9,"18:00":14},"appointmentTypeBreakdown":{"PERSONAL_TRAINING":28,"CONSULTATION":8,"CHECK_IN":6}}}}},"description":"Success"}},"summary":"Appointment analytics for a date window","tags":["Appointment"]}},"/v1/scheduling/appointments/filters":{"get":{"operationId":"AppointmentController.getAppointmentFilterOptions","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/AppointmentFiltersResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Appointment filter options retrieved successfully","data":{"appointmentTypes":["PERSONAL_TRAINING","GROUP_SESSION","CONSULTATION","CUSTOM","CHECK_IN","LEAD","CLIENT_REMINDER"],"statusOptions":["SCHEDULED","CONFIRMED","COMPLETED","CANCELLED","RESCHEDULED","NO_SHOW","IN_PROGRESS"],"modalityOptions":["IN_PERSON","VIRTUAL","HYBRID","ASYNCHRONOUS"],"cancellationReasons":["CLIENT_REQUEST","TRAINER_REQUEST","EMERGENCY","ILLNESS","SCHEDULING_CONFLICT","TECHNICAL_ISSUES","WEATHER","NO_REASON_PROVIDED","OTHER"]}}}},"description":"Success"}},"summary":"Static filter option lists (types, statuses, modalities, cancellation reasons)","tags":["Appointment"]}},"/v1/scheduling/appointments/external-busy":{"get":{"operationId":"AppointmentController.getExternalBusy","parameters":[{"in":"query","name":"startDate","required":false,"schema":{"type":"string"}},{"in":"query","name":"endDate","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"External busy intervals retrieved successfully","data":{"busy":[{"start":"2026-01-20T12:00:00.000Z","end":"2026-01-20T13:00:00.000Z"}]}}}},"description":"Success"}},"summary":"External Google 'busy' intervals for the current coach's calendar","tags":["Appointment"]}},"/v1/scheduling/appointments/{appointmentId}":{"get":{"operationId":"AppointmentController.getAppointmentById","parameters":[{"in":"path","name":"appointmentId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Appointment retrieved successfully","data":{"id":"7c1f2a3b-4d5e-6f70-8a91-b2c3d4e5f601","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","startTime":"2026-01-20T14:00:00.000Z","endTime":"2026-01-20T15:00:00.000Z","title":"Personal Training Session","description":"Lower-body strength + mobility.","type":"PERSONAL_TRAINING","status":"SCHEDULED","modality":"IN_PERSON","location":"Downtown Studio, Floor 2","isRecurring":false,"reminderSent":false,"checkInRequired":false,"trainer":{"id":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","firstName":"Dijana","lastName":"Keri","email":"dijana@protocolcrm.com"},"participantInfos":[{"userId":"2b3c4d5e-6f70-8a91-b2c3-d4e5f6071829","firstName":"Marko","lastName":"Petrovic","email":"marko@example.com"}],"tenantId":"f0e1d2c3-b4a5-6879-8a9b-0c1d2e3f4a5b"}}}},"description":"Success"}},"summary":"Get an appointment by id (404 with a hand-built envelope when missing)","tags":["Appointment"]},"put":{"operationId":"AppointmentController.updateAppointment","parameters":[{"in":"path","name":"appointmentId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentDto"},"example":{"startTime":"2026-01-20T14:00:00.000Z","endTime":"2026-01-20T15:00:00.000Z","title":"Personal Training Session","type":"PERSONAL_TRAINING","modality":"IN_PERSON","location":"Downtown Studio, Floor 2","status":"CONFIRMED"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/AppointmentDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Appointment updated successfully","data":{"id":"7c1f2a3b-4d5e-6f70-8a91-b2c3d4e5f601","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","startTime":"2026-01-20T14:00:00.000Z","endTime":"2026-01-20T15:00:00.000Z","title":"Personal Training Session","description":"Lower-body strength + mobility.","type":"PERSONAL_TRAINING","status":"SCHEDULED","modality":"IN_PERSON","location":"Downtown Studio, Floor 2","isRecurring":false,"reminderSent":false,"checkInRequired":false,"trainer":{"id":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","firstName":"Dijana","lastName":"Keri","email":"dijana@protocolcrm.com"},"participantInfos":[{"userId":"2b3c4d5e-6f70-8a91-b2c3-d4e5f6071829","firstName":"Marko","lastName":"Petrovic","email":"marko@example.com"}],"tenantId":"f0e1d2c3-b4a5-6879-8a9b-0c1d2e3f4a5b"}}}},"description":"Success"}},"summary":"Update an appointment (supports recurring updateStrategy)","tags":["Appointment"]},"delete":{"operationId":"AppointmentController.deleteAppointment","parameters":[{"in":"path","name":"appointmentId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"query","name":"updateStrategy","required":false,"schema":{"type":"string"}},{"in":"query","name":"occurrenceDateTime","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Appointment deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete an appointment (or a recurring occurrence via updateStrategy)","tags":["Appointment"]}},"/v1/scheduling/appointments/{appointmentId}/send-reminder":{"post":{"operationId":"AppointmentController.sendReminderNow","parameters":[{"in":"path","name":"appointmentId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Reminder sent","data":{"created":true,"occurrenceTime":"2026-01-20T14:00:00.000Z","formId":"9d8c7b6a-5f4e-3d2c-1b0a-9e8d7c6b5a40"}}}},"description":"Success"}},"summary":"Trigger the appointment reminder form immediately","tags":["Appointment"]}},"/v1/scheduling/google-calendar/connect-url":{"get":{"operationId":"GoogleCalendarController.getConnectUrl","parameters":[{"in":"query","name":"redirectOnSuccess","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Success","data":{"authorizationUrl":"https://accounts.google.com/o/oauth2/v2/auth?client_id=...&scope=calendar.events&state=..."}}}},"description":"Success"}},"summary":"Build the Google consent URL with calendar scopes (incremental auth)","tags":["Google Calendar"]}},"/v1/scheduling/google-calendar/calendars":{"get":{"operationId":"GoogleCalendarController.listCalendars","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Success","data":[{"id":"coach@gmail.com","summary":"Coaching","primary":true},{"id":"family@group.calendar.google.com","summary":"Family"}]}}},"description":"Success"}},"summary":"List the connected account's Google calendars (for the picker UI)","tags":["Google Calendar"]}},"/v1/scheduling/google-calendar/":{"get":{"operationId":"GoogleCalendarController.getStatus","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Success","data":{"connected":true,"hasCalendarScopes":true,"googleEmail":"coach@gmail.com","calendarSyncEnabled":true,"syncCalendarId":"coach@gmail.com","busyCalendarIds":["coach@gmail.com","family@group.calendar.google.com"],"eventTitleMode":"generic","lastSyncError":null}}}},"description":"Success"}},"summary":"Get the current coach Google Calendar sync status","tags":["Google Calendar"]},"put":{"operationId":"GoogleCalendarController.updateSettings","requestBody":{"content":{"application/json":{"schema":{},"example":{"calendarSyncEnabled":true,"syncCalendarId":"coach@gmail.com","busyCalendarIds":["coach@gmail.com"],"eventTitleMode":"initials"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Success","data":{"connected":true,"hasCalendarScopes":true,"googleEmail":"coach@gmail.com","calendarSyncEnabled":true,"syncCalendarId":"coach@gmail.com","busyCalendarIds":["coach@gmail.com","family@group.calendar.google.com"],"eventTitleMode":"generic","lastSyncError":null}}}},"description":"Success"}},"summary":"Update Google Calendar sync settings (enabling backfills future appointments)","tags":["Google Calendar"]},"delete":{"operationId":"GoogleCalendarController.disconnect","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Success","data":{"connected":false,"hasCalendarScopes":true,"googleEmail":"coach@gmail.com","calendarSyncEnabled":false,"syncCalendarId":"coach@gmail.com","busyCalendarIds":["coach@gmail.com","family@group.calendar.google.com"],"eventTitleMode":"generic","lastSyncError":null}}}},"description":"Success"}},"summary":"Disconnect Google Calendar sync for the current coach","tags":["Google Calendar"]}},"/v1/communication/conversations/":{"post":{"operationId":"ConversationController.create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationDto"},"example":{"type":"DIRECT","participants":[{"id":"b2c3d4e5-f6a7-4b2c-9d3e-4f5a6b7c8d9e","canPost":true}],"metadata":{}}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ConversationDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Conversation created successfully","data":{"id":"7c9e6679-7425-40de-944b-e07fc1f90ae7","createdAt":"2026-01-10T08:00:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","title":null,"type":"DIRECT","isActive":true,"lastActivityAt":"2026-01-15T09:30:00.000Z","creatorId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","image":null,"participants":[{"id":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","name":"Jane Coach","avatarUrl":null,"role":"OWNER","canPost":true},{"id":"b2c3d4e5-f6a7-4b2c-9d3e-4f5a6b7c8d9e","name":"Alex Rivera","avatarUrl":null,"role":"MEMBER","canPost":true}],"lastMessage":{"id":"d4e5f6a7-b8c9-4d3e-af4b-5a6b7c8d9e0f","createdAt":null,"updatedAt":null,"content":"See you at tomorrow’s session!","type":"CHAT","source":"USER","senderId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","senderName":"Jane Coach","status":null,"sentAt":"2026-01-15T09:30:00.000Z","isPinned":null,"unreadCount":0},"metadata":{},"draft":null}}}},"description":"Success"}},"summary":"Create a conversation","tags":["Conversation"]},"get":{"operationId":"ConversationController.list","parameters":[{"in":"query","name":"page","required":false,"schema":{"type":"number"}},{"in":"query","name":"pageSize","required":false,"schema":{"type":"number"}},{"in":"query","name":"type","required":false,"schema":{"type":"string"}},{"in":"query","name":"userId","required":false,"schema":{"type":"string"}},{"in":"query","name":"creatorId","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Conversations retrieved successfully","data":{"items":[{"id":"7c9e6679-7425-40de-944b-e07fc1f90ae7","createdAt":"2026-01-10T08:00:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","title":null,"type":"DIRECT","isActive":true,"lastActivityAt":"2026-01-15T09:30:00.000Z","creatorId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","image":null,"participants":[{"id":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","name":"Jane Coach","avatarUrl":null,"role":"OWNER","canPost":true},{"id":"b2c3d4e5-f6a7-4b2c-9d3e-4f5a6b7c8d9e","name":"Alex Rivera","avatarUrl":null,"role":"MEMBER","canPost":true}],"lastMessage":{"id":"d4e5f6a7-b8c9-4d3e-af4b-5a6b7c8d9e0f","createdAt":null,"updatedAt":null,"content":"See you at tomorrow’s session!","type":"CHAT","source":"USER","senderId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","senderName":"Jane Coach","status":null,"sentAt":"2026-01-15T09:30:00.000Z","isPinned":null,"unreadCount":0},"metadata":{},"draft":null}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List conversations (paginated, 1-indexed, lastActivityAt DESC)","tags":["Conversation"]}},"/v1/communication/conversations/{conversationId}/participants":{"post":{"operationId":"ConversationController.addParticipants","parameters":[{"in":"path","name":"conversationId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"},"example":["b2c3d4e5-f6a7-4b2c-9d3e-4f5a6b7c8d9e"]}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ConversationDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Participants added successfully","data":{"id":"7c9e6679-7425-40de-944b-e07fc1f90ae7","createdAt":"2026-01-10T08:00:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","title":null,"type":"DIRECT","isActive":true,"lastActivityAt":"2026-01-15T09:30:00.000Z","creatorId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","image":null,"participants":[{"id":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","name":"Jane Coach","avatarUrl":null,"role":"OWNER","canPost":true},{"id":"b2c3d4e5-f6a7-4b2c-9d3e-4f5a6b7c8d9e","name":"Alex Rivera","avatarUrl":null,"role":"MEMBER","canPost":true}],"lastMessage":{"id":"d4e5f6a7-b8c9-4d3e-af4b-5a6b7c8d9e0f","createdAt":null,"updatedAt":null,"content":"See you at tomorrow’s session!","type":"CHAT","source":"USER","senderId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","senderName":"Jane Coach","status":null,"sentAt":"2026-01-15T09:30:00.000Z","isPinned":null,"unreadCount":0},"metadata":{},"draft":null}}}},"description":"Success"}},"summary":"Add participants (body: array of user ids)","tags":["Conversation"]},"delete":{"operationId":"ConversationController.removeParticipants","parameters":[{"in":"path","name":"conversationId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"items":{"type":"object"},"type":"array"},"example":["b2c3d4e5-f6a7-4b2c-9d3e-4f5a6b7c8d9e"]}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ConversationDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Participants removed successfully","data":{"id":"7c9e6679-7425-40de-944b-e07fc1f90ae7","createdAt":"2026-01-10T08:00:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","title":null,"type":"DIRECT","isActive":true,"lastActivityAt":"2026-01-15T09:30:00.000Z","creatorId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","image":null,"participants":[{"id":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","name":"Jane Coach","avatarUrl":null,"role":"OWNER","canPost":true},{"id":"b2c3d4e5-f6a7-4b2c-9d3e-4f5a6b7c8d9e","name":"Alex Rivera","avatarUrl":null,"role":"MEMBER","canPost":true}],"lastMessage":{"id":"d4e5f6a7-b8c9-4d3e-af4b-5a6b7c8d9e0f","createdAt":null,"updatedAt":null,"content":"See you at tomorrow’s session!","type":"CHAT","source":"USER","senderId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","senderName":"Jane Coach","status":null,"sentAt":"2026-01-15T09:30:00.000Z","isPinned":null,"unreadCount":0},"metadata":{},"draft":null}}}},"description":"Success"}},"summary":"Remove participants (body: array of user ids)","tags":["Conversation"]}},"/v1/communication/conversations/{conversationId}/participants/{userId}/role":{"put":{"operationId":"ConversationController.setParticipantRole","parameters":[{"in":"path","name":"conversationId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{},"example":{"role":"MEMBER","canPost":false}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ConversationDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Participant role updated successfully","data":{"id":"7c9e6679-7425-40de-944b-e07fc1f90ae7","createdAt":"2026-01-10T08:00:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","title":null,"type":"DIRECT","isActive":true,"lastActivityAt":"2026-01-15T09:30:00.000Z","creatorId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","image":null,"participants":[{"id":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","name":"Jane Coach","avatarUrl":null,"role":"OWNER","canPost":true},{"id":"b2c3d4e5-f6a7-4b2c-9d3e-4f5a6b7c8d9e","name":"Alex Rivera","avatarUrl":null,"role":"MEMBER","canPost":true}],"lastMessage":{"id":"d4e5f6a7-b8c9-4d3e-af4b-5a6b7c8d9e0f","createdAt":null,"updatedAt":null,"content":"See you at tomorrow’s session!","type":"CHAT","source":"USER","senderId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","senderName":"Jane Coach","status":null,"sentAt":"2026-01-15T09:30:00.000Z","isPinned":null,"unreadCount":0},"metadata":{},"draft":null}}}},"description":"Success"}},"summary":"Set a participant’s role / posting permission","tags":["Conversation"]}},"/v1/communication/conversations/{conversationId}/draft":{"get":{"operationId":"ConversationController.getDraft","parameters":[{"in":"path","name":"conversationId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/DraftDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Draft retrieved successfully","data":{"conversationId":"7c9e6679-7425-40de-944b-e07fc1f90ae7","content":"Great work this week — keep the protein up and ","entities":null,"media":null,"contentFormat":1,"updatedById":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","updatedByName":"Jane Coach","updatedAt":"2026-01-15T09:30:00.000Z"}}}},"description":"Success"}},"summary":"Get the acting identity’s composer draft (null when none)","tags":["Conversation"]},"put":{"operationId":"ConversationController.saveDraft","parameters":[{"in":"path","name":"conversationId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DraftUpsertBody"},"example":{"content":"Great work this week — keep the protein up and ","contentFormat":1}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/DraftDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Draft saved successfully","data":{"conversationId":"7c9e6679-7425-40de-944b-e07fc1f90ae7","content":"Great work this week — keep the protein up and ","entities":null,"media":null,"contentFormat":1,"updatedById":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","updatedByName":"Jane Coach","updatedAt":"2026-01-15T09:30:00.000Z"}}}},"description":"Success"}},"summary":"Save the composer draft (empty content clears it → null)","tags":["Conversation"]},"delete":{"operationId":"ConversationController.clearDraft","parameters":[{"in":"path","name":"conversationId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Draft cleared successfully","data":null}}},"description":"Success"}},"summary":"Clear the composer draft","tags":["Conversation"]}},"/v1/communication/conversations/{conversationId}/presence":{"get":{"operationId":"ConversationController.presence","parameters":[{"in":"path","name":"conversationId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Conversation presence retrieved successfully","data":{"conversationId":"7c9e6679-7425-40de-944b-e07fc1f90ae7","users":[{"userId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","isOnline":true,"lastSeen":"2026-01-15T09:29:00.000Z"},{"userId":"b2c3d4e5-f6a7-4b2c-9d3e-4f5a6b7c8d9e","isOnline":false,"lastSeen":null}],"totalParticipants":2}}}},"description":"Success"}},"summary":"Get online presence for the conversation’s participants","tags":["Conversation"]}},"/v1/communication/conversations/{conversationId}/media":{"get":{"operationId":"ConversationController.media","parameters":[{"in":"path","name":"conversationId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"query","name":"kind","required":false,"schema":{"type":"string"}},{"in":"query","name":"page","required":false,"schema":{"type":"number"}},{"in":"query","name":"pageSize","required":false,"schema":{"type":"number"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Conversation media retrieved successfully","data":{"items":[{"kind":"MEDIA","messageId":"d4e5f6a7-b8c9-4d3e-af4b-5a6b7c8d9e0f","senderId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","sentAt":"2026-01-15T09:30:00.000Z","media":{"id":"e5f6a7b8-c9d0-4e4f-ba5b-6c7d8e9f0a1b","type":"IMAGE","url":"https://cdn.example/img.png","name":"form-check.png"}}],"total":1,"page":1,"pageSize":30,"totalPages":1}}}},"description":"Success"}},"summary":"List shared media / files / links (kind=MEDIA|FILE|LINK, paginated)","tags":["Conversation"]}},"/v1/communication/conversations/{conversationId}":{"get":{"operationId":"ConversationController.getOne","parameters":[{"in":"path","name":"conversationId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Conversation retrieved successfully","data":{"id":"7c9e6679-7425-40de-944b-e07fc1f90ae7","createdAt":"2026-01-10T08:00:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","title":null,"type":"DIRECT","isActive":true,"lastActivityAt":"2026-01-15T09:30:00.000Z","creatorId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","image":null,"participants":[{"id":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","name":"Jane Coach","avatarUrl":null,"role":"OWNER","canPost":true},{"id":"b2c3d4e5-f6a7-4b2c-9d3e-4f5a6b7c8d9e","name":"Alex Rivera","avatarUrl":null,"role":"MEMBER","canPost":true}],"lastMessage":{"id":"d4e5f6a7-b8c9-4d3e-af4b-5a6b7c8d9e0f","createdAt":null,"updatedAt":null,"content":"See you at tomorrow’s session!","type":"CHAT","source":"USER","senderId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","senderName":"Jane Coach","status":null,"sentAt":"2026-01-15T09:30:00.000Z","isPinned":null,"unreadCount":0},"metadata":{},"draft":null}}}},"description":"Success"}},"summary":"Get a conversation by id (404 when not found)","tags":["Conversation"]},"put":{"operationId":"ConversationController.update","parameters":[{"in":"path","name":"conversationId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConversationDto"},"example":{"title":"Weekly check-ins","isActive":true,"metadata":{"pinned":true}}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ConversationDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Conversation updated successfully","data":{"id":"7c9e6679-7425-40de-944b-e07fc1f90ae7","createdAt":"2026-01-10T08:00:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","title":null,"type":"DIRECT","isActive":true,"lastActivityAt":"2026-01-15T09:30:00.000Z","creatorId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","image":null,"participants":[{"id":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","name":"Jane Coach","avatarUrl":null,"role":"OWNER","canPost":true},{"id":"b2c3d4e5-f6a7-4b2c-9d3e-4f5a6b7c8d9e","name":"Alex Rivera","avatarUrl":null,"role":"MEMBER","canPost":true}],"lastMessage":{"id":"d4e5f6a7-b8c9-4d3e-af4b-5a6b7c8d9e0f","createdAt":null,"updatedAt":null,"content":"See you at tomorrow’s session!","type":"CHAT","source":"USER","senderId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","senderName":"Jane Coach","status":null,"sentAt":"2026-01-15T09:30:00.000Z","isPinned":null,"unreadCount":0},"metadata":{},"draft":null}}}},"description":"Success"}},"summary":"Update a conversation (title / isActive / image / metadata / participants)","tags":["Conversation"]},"delete":{"operationId":"ConversationController.remove","parameters":[{"in":"path","name":"conversationId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Conversation deleted successfully","data":null}}},"description":"Success"}},"summary":"Soft-delete a conversation (isActive=false)","tags":["Conversation"]}},"/v1/communication/messages/":{"post":{"operationId":"MessageController.create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageDto"},"example":{"conversationId":"7c9e6679-7425-40de-944b-e07fc1f90ae7","content":"Hey, how did today’s session feel?"}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/MessageDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Message sent successfully","data":{"id":"d4e5f6a7-b8c9-4d3e-af4b-5a6b7c8d9e0f","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","content":"Hey, how did today’s session feel?","type":"CHAT","source":"USER","senderId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","senderName":"Jane Coach","senderAvatarUrl":null,"conversationId":"7c9e6679-7425-40de-944b-e07fc1f90ae7","sentAt":"2026-01-15T09:30:00.000Z","participantStatuses":[{"participantId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","status":"READ","timestamp":"2026-01-15T09:30:00.000Z"},{"participantId":"b2c3d4e5-f6a7-4b2c-9d3e-4f5a6b7c8d9e","status":"DELIVERED","timestamp":"2026-01-15T09:30:00.000Z"}],"media":null,"parentMessageId":null,"metadata":{},"entities":null,"contentFormat":1,"editedAt":null}}}},"description":"Success"}},"summary":"Send a message","tags":["Message"]}},"/v1/communication/messages/conversation/{conversationId}":{"get":{"operationId":"MessageController.getForConversation","parameters":[{"in":"path","name":"conversationId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"query","name":"page","required":false,"schema":{"type":"number"}},{"in":"query","name":"pageSize","required":false,"schema":{"type":"number"}},{"in":"query","name":"latest","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Messages retrieved successfully","data":{"items":[{"id":"d4e5f6a7-b8c9-4d3e-af4b-5a6b7c8d9e0f","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","content":"Hey, how did today’s session feel?","type":"CHAT","source":"USER","senderId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","senderName":"Jane Coach","senderAvatarUrl":null,"conversationId":"7c9e6679-7425-40de-944b-e07fc1f90ae7","sentAt":"2026-01-15T09:30:00.000Z","participantStatuses":[{"participantId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","status":"READ","timestamp":"2026-01-15T09:30:00.000Z"},{"participantId":"b2c3d4e5-f6a7-4b2c-9d3e-4f5a6b7c8d9e","status":"DELIVERED","timestamp":"2026-01-15T09:30:00.000Z"}],"media":null,"parentMessageId":null,"metadata":{},"entities":null,"contentFormat":1,"editedAt":null}],"total":1,"page":1,"pageSize":50,"totalPages":1}}}},"description":"Success"}},"summary":"List messages in a conversation (paginated, 1-indexed, sentAt ASC)","tags":["Message"]}},"/v1/communication/messages/conversation/{conversationId}/mark-unread":{"post":{"operationId":"MessageController.markConversationUnread","parameters":[{"in":"path","name":"conversationId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Conversation marked as unread","data":{"unreadCount":3}}}},"description":"Success"}},"summary":"Mark a conversation as unread for the caller","tags":["Message"]}},"/v1/communication/messages/{messageId}/forward":{"post":{"operationId":"MessageController.forward","parameters":[{"in":"path","name":"messageId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForwardMessageRequestDto"},"example":{"conversationIds":["7c9e6679-7425-40de-944b-e07fc1f90ae7"],"forwardMessage":"Sharing this with you —"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Message forwarded successfully","data":null}}},"description":"Success"}},"summary":"Forward a message to one or more conversations","tags":["Message"]}},"/v1/communication/messages/{messageId}":{"get":{"operationId":"MessageController.getOne","parameters":[{"in":"path","name":"messageId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Message retrieved successfully","data":{"id":"d4e5f6a7-b8c9-4d3e-af4b-5a6b7c8d9e0f","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","content":"Hey, how did today’s session feel?","type":"CHAT","source":"USER","senderId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","senderName":"Jane Coach","senderAvatarUrl":null,"conversationId":"7c9e6679-7425-40de-944b-e07fc1f90ae7","sentAt":"2026-01-15T09:30:00.000Z","participantStatuses":[{"participantId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","status":"READ","timestamp":"2026-01-15T09:30:00.000Z"},{"participantId":"b2c3d4e5-f6a7-4b2c-9d3e-4f5a6b7c8d9e","status":"DELIVERED","timestamp":"2026-01-15T09:30:00.000Z"}],"media":null,"parentMessageId":null,"metadata":{},"entities":null,"contentFormat":1,"editedAt":null}}}},"description":"Success"}},"summary":"Get a message by id (404 when not found)","tags":["Message"]},"put":{"operationId":"MessageController.update","parameters":[{"in":"path","name":"messageId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageDto"},"example":{"content":"Hey, how did the session feel? (edited)"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/MessageDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Message updated successfully","data":{"id":"d4e5f6a7-b8c9-4d3e-af4b-5a6b7c8d9e0f","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","content":"Hey, how did the session feel? (edited)","type":"CHAT","source":"USER","senderId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","senderName":"Jane Coach","senderAvatarUrl":null,"conversationId":"7c9e6679-7425-40de-944b-e07fc1f90ae7","sentAt":"2026-01-15T09:30:00.000Z","participantStatuses":[{"participantId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","status":"READ","timestamp":"2026-01-15T09:30:00.000Z"},{"participantId":"b2c3d4e5-f6a7-4b2c-9d3e-4f5a6b7c8d9e","status":"DELIVERED","timestamp":"2026-01-15T09:30:00.000Z"}],"media":null,"parentMessageId":null,"metadata":{},"entities":null,"contentFormat":1,"editedAt":"2026-01-15T10:00:00.000Z"}}}},"description":"Success"}},"summary":"Edit a message (content / media / metadata / entities)","tags":["Message"]},"delete":{"operationId":"MessageController.remove","parameters":[{"in":"path","name":"messageId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Message deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete a message","tags":["Message"]}},"/v1/communication/message-templates/":{"get":{"operationId":"MessageTemplateController.list","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Message templates retrieved successfully","data":[{"id":"f6a7b8c9-d0e1-4f5a-bb6c-7d8e9f0a1b2c","title":"Welcome","content":"Hi {{firstName}}, welcome aboard! Let’s get your first check-in scheduled.","entities":null,"media":null,"createdBy":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z"}]}}},"description":"Success"}},"summary":"List message templates for the tenant","tags":["Message Template"]},"post":{"operationId":"MessageTemplateController.create","requestBody":{"content":{"application/json":{"schema":{},"example":{"title":"Welcome","content":"Hi {{firstName}}, welcome aboard! Let’s get your first check-in scheduled."}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"example":{"success":true,"message":"Message template created successfully","data":{"id":"f6a7b8c9-d0e1-4f5a-bb6c-7d8e9f0a1b2c","title":"Welcome","content":"Hi {{firstName}}, welcome aboard! Let’s get your first check-in scheduled.","entities":null,"media":null,"createdBy":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z"}}}},"description":"Success"}},"summary":"Create a message template","tags":["Message Template"]}},"/v1/communication/message-templates/{templateId}":{"get":{"operationId":"MessageTemplateController.getOne","parameters":[{"in":"path","name":"templateId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Message template retrieved successfully","data":{"id":"f6a7b8c9-d0e1-4f5a-bb6c-7d8e9f0a1b2c","title":"Welcome","content":"Hi {{firstName}}, welcome aboard! Let’s get your first check-in scheduled.","entities":null,"media":null,"createdBy":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z"}}}},"description":"Success"}},"summary":"Get a message template by id","tags":["Message Template"]},"put":{"operationId":"MessageTemplateController.update","parameters":[{"in":"path","name":"templateId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{},"example":{"title":"Welcome (v2)","content":"Hi {{firstName}}, welcome aboard! Let’s get your first check-in scheduled."}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Message template updated successfully","data":{"id":"f6a7b8c9-d0e1-4f5a-bb6c-7d8e9f0a1b2c","title":"Welcome (v2)","content":"Hi {{firstName}}, welcome aboard! Let’s get your first check-in scheduled.","entities":null,"media":null,"createdBy":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z"}}}},"description":"Success"}},"summary":"Update a message template","tags":["Message Template"]},"delete":{"operationId":"MessageTemplateController.delete","parameters":[{"in":"path","name":"templateId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Message template deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete a message template","tags":["Message Template"]}},"/v1/automations/":{"get":{"operationId":"AutomationController.list","parameters":[{"in":"query","name":"kind","required":false,"schema":{"type":"string"}},{"in":"query","name":"status","required":false,"schema":{"type":"string"}},{"in":"query","name":"page","required":false,"schema":{"type":"number"}},{"in":"query","name":"size","required":false,"schema":{"type":"number"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Automations fetched","data":{"items":[{"id":"b7f3c2a1-9d8e-4f60-8a71-2c3d4e5f6071","tenantId":"f0e1d2c3-b4a5-6879-8a9b-0c1d2e3f4a5b","name":"Weekly progress report","kind":"PROGRESS_REPORT","status":"ACTIVE","config":{"dataInputs":{"previousEntriesCount":2,"includeOnboardingAnswers":true,"includeClientProfileSnapshot":true,"recentChatMessagesCount":0}},"triggerConfig":{"trainerIds":["a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"]},"triggerCount":12,"lastTriggeredAt":"2026-01-14T08:00:00.000Z","createdAt":"2026-01-01T09:30:00.000Z","updatedAt":"2026-01-14T08:00:00.000Z"}],"total":1,"page":0,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List automations (0-indexed Spring-style pagination)","tags":["Automation"]},"post":{"operationId":"AutomationController.create","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAutomationRequest"},"example":{"name":"Weekly progress report","kind":"PROGRESS_REPORT","config":{"dataInputs":{"previousEntriesCount":2,"recentChatMessagesCount":0}},"triggerConfig":{"trainerIds":["a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"]}}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/AutomationDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Automation created","data":{"id":"b7f3c2a1-9d8e-4f60-8a71-2c3d4e5f6071","tenantId":"f0e1d2c3-b4a5-6879-8a9b-0c1d2e3f4a5b","name":"Weekly progress report","kind":"PROGRESS_REPORT","status":"DRAFT","config":{"dataInputs":{"previousEntriesCount":2,"includeOnboardingAnswers":true,"includeClientProfileSnapshot":true,"recentChatMessagesCount":0}},"triggerConfig":{"trainerIds":["a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"]},"triggerCount":0,"lastTriggeredAt":"2026-01-14T08:00:00.000Z","createdAt":"2026-01-01T09:30:00.000Z","updatedAt":"2026-01-14T08:00:00.000Z"}}}},"description":"Success"}},"summary":"Create an automation","tags":["Automation"]}},"/v1/automations/kinds":{"get":{"operationId":"AutomationController.listKinds","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/KindMetadataDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Kinds fetched","data":[{"kind":"PROGRESS_REPORT","displayName":"Progress Report","supportedTriggers":["PROGRESS_ENTRY_CREATED","MANUAL"]}]}}},"description":"Success"}},"summary":"List available automation kinds and their supported triggers","tags":["Automation"]}},"/v1/automations/runs/{runId}":{"get":{"operationId":"AutomationController.getRun","parameters":[{"in":"path","name":"runId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/AutomationRunDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Run fetched","data":{"id":"3d2c1b0a-9f8e-4d6c-8b5a-1029384756af","tenantId":"f0e1d2c3-b4a5-6879-8a9b-0c1d2e3f4a5b","automationId":"b7f3c2a1-9d8e-4f60-8a71-2c3d4e5f6071","status":"COMPLETED","triggerSource":"MANUAL","triggerData":{"entryId":"5e6f7081-92a3-4b5c-8d6e-7f8091a2b3c4"},"gatheredData":{"previousEntries":2},"prompt":"Summarize this week of progress for the client...","rawResponse":"{\"client_facing_summary\":\"Great week...\",\"priority\":\"STANDARD\"}","parsedOutput":{"priority":"STANDARD"},"outputEntityType":"CLIENT_INSIGHT","outputEntityId":"6f708192-a3b4-4c5d-8e6f-8091a2b3c4d5","inputTokens":1840,"outputTokens":320,"errorMessage":null,"startedAt":"2026-01-14T08:00:00.000Z","completedAt":"2026-01-14T08:00:07.000Z"}}}},"description":"Success"}},"summary":"Get a single automation run by id","tags":["Automation"]}},"/v1/automations/{id}":{"get":{"operationId":"AutomationController.getOne","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/AutomationDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Automation fetched","data":{"id":"b7f3c2a1-9d8e-4f60-8a71-2c3d4e5f6071","tenantId":"f0e1d2c3-b4a5-6879-8a9b-0c1d2e3f4a5b","name":"Weekly progress report","kind":"PROGRESS_REPORT","status":"ACTIVE","config":{"dataInputs":{"previousEntriesCount":2,"includeOnboardingAnswers":true,"includeClientProfileSnapshot":true,"recentChatMessagesCount":0}},"triggerConfig":{"trainerIds":["a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"]},"triggerCount":12,"lastTriggeredAt":"2026-01-14T08:00:00.000Z","createdAt":"2026-01-01T09:30:00.000Z","updatedAt":"2026-01-14T08:00:00.000Z"}}}},"description":"Success"}},"summary":"Get an automation by id","tags":["Automation"]},"put":{"operationId":"AutomationController.update","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAutomationRequest"},"example":{"name":"Weekly progress report (revised)","config":{"dataInputs":{"previousEntriesCount":3}}}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/AutomationDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Automation updated","data":{"id":"b7f3c2a1-9d8e-4f60-8a71-2c3d4e5f6071","tenantId":"f0e1d2c3-b4a5-6879-8a9b-0c1d2e3f4a5b","name":"Weekly progress report","kind":"PROGRESS_REPORT","status":"ACTIVE","config":{"dataInputs":{"previousEntriesCount":2,"includeOnboardingAnswers":true,"includeClientProfileSnapshot":true,"recentChatMessagesCount":0}},"triggerConfig":{"trainerIds":["a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"]},"triggerCount":12,"lastTriggeredAt":"2026-01-14T08:00:00.000Z","createdAt":"2026-01-01T09:30:00.000Z","updatedAt":"2026-01-14T08:00:00.000Z"}}}},"description":"Success"}},"summary":"Update an automation (partial)","tags":["Automation"]},"delete":{"operationId":"AutomationController.delete","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{}},"description":"Successful response"},"204":{"description":"Success","content":{"application/json":{}}}},"summary":"Delete an automation (cascades its run history)","tags":["Automation"]}},"/v1/automations/{id}/activate":{"post":{"operationId":"AutomationController.activate","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/AutomationDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Automation activated","data":{"id":"b7f3c2a1-9d8e-4f60-8a71-2c3d4e5f6071","tenantId":"f0e1d2c3-b4a5-6879-8a9b-0c1d2e3f4a5b","name":"Weekly progress report","kind":"PROGRESS_REPORT","status":"ACTIVE","config":{"dataInputs":{"previousEntriesCount":2,"includeOnboardingAnswers":true,"includeClientProfileSnapshot":true,"recentChatMessagesCount":0}},"triggerConfig":{"trainerIds":["a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"]},"triggerCount":12,"lastTriggeredAt":"2026-01-14T08:00:00.000Z","createdAt":"2026-01-01T09:30:00.000Z","updatedAt":"2026-01-14T08:00:00.000Z"}}}},"description":"Success"}},"summary":"Activate an automation (status -> ACTIVE)","tags":["Automation"]}},"/v1/automations/{id}/pause":{"post":{"operationId":"AutomationController.pause","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/AutomationDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Automation paused","data":{"id":"b7f3c2a1-9d8e-4f60-8a71-2c3d4e5f6071","tenantId":"f0e1d2c3-b4a5-6879-8a9b-0c1d2e3f4a5b","name":"Weekly progress report","kind":"PROGRESS_REPORT","status":"PAUSED","config":{"dataInputs":{"previousEntriesCount":2,"includeOnboardingAnswers":true,"includeClientProfileSnapshot":true,"recentChatMessagesCount":0}},"triggerConfig":{"trainerIds":["a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"]},"triggerCount":12,"lastTriggeredAt":"2026-01-14T08:00:00.000Z","createdAt":"2026-01-01T09:30:00.000Z","updatedAt":"2026-01-14T08:00:00.000Z"}}}},"description":"Success"}},"summary":"Pause an automation (status -> PAUSED)","tags":["Automation"]}},"/v1/automations/{id}/archive":{"post":{"operationId":"AutomationController.archive","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/AutomationDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Automation archived","data":{"id":"b7f3c2a1-9d8e-4f60-8a71-2c3d4e5f6071","tenantId":"f0e1d2c3-b4a5-6879-8a9b-0c1d2e3f4a5b","name":"Weekly progress report","kind":"PROGRESS_REPORT","status":"ARCHIVED","config":{"dataInputs":{"previousEntriesCount":2,"includeOnboardingAnswers":true,"includeClientProfileSnapshot":true,"recentChatMessagesCount":0}},"triggerConfig":{"trainerIds":["a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"]},"triggerCount":12,"lastTriggeredAt":"2026-01-14T08:00:00.000Z","createdAt":"2026-01-01T09:30:00.000Z","updatedAt":"2026-01-14T08:00:00.000Z"}}}},"description":"Success"}},"summary":"Archive an automation (status -> ARCHIVED)","tags":["Automation"]}},"/v1/automations/{id}/run":{"post":{"operationId":"AutomationController.manualRun","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManualRunRequest"},"example":{"triggerData":{"entryId":"5e6f7081-92a3-4b5c-8d6e-7f8091a2b3c4"}}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ManualRunResponse"}},"required":["success","message","data"]},"example":{"success":true,"message":"Automation run dispatched","data":{"runId":"3d2c1b0a-9f8e-4d6c-8b5a-1029384756af","automationId":"b7f3c2a1-9d8e-4f60-8a71-2c3d4e5f6071"}}}},"description":"Success"}},"summary":"Dispatch a manual automation run","tags":["Automation"]}},"/v1/automations/{id}/runs":{"get":{"operationId":"AutomationController.listRuns","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"query","name":"status","required":false,"schema":{"type":"string"}},{"in":"query","name":"page","required":false,"schema":{"type":"number"}},{"in":"query","name":"size","required":false,"schema":{"type":"number"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Runs fetched","data":{"items":[{"id":"3d2c1b0a-9f8e-4d6c-8b5a-1029384756af","tenantId":"f0e1d2c3-b4a5-6879-8a9b-0c1d2e3f4a5b","automationId":"b7f3c2a1-9d8e-4f60-8a71-2c3d4e5f6071","status":"COMPLETED","triggerSource":"MANUAL","triggerData":{"entryId":"5e6f7081-92a3-4b5c-8d6e-7f8091a2b3c4"},"gatheredData":{"previousEntries":2},"prompt":"Summarize this week of progress for the client...","rawResponse":"{\"client_facing_summary\":\"Great week...\",\"priority\":\"STANDARD\"}","parsedOutput":{"priority":"STANDARD"},"outputEntityType":"CLIENT_INSIGHT","outputEntityId":"6f708192-a3b4-4c5d-8e6f-8091a2b3c4d5","inputTokens":1840,"outputTokens":320,"errorMessage":null,"startedAt":"2026-01-14T08:00:00.000Z","completedAt":"2026-01-14T08:00:07.000Z"}],"total":1,"page":0,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List runs for an automation (0-indexed pagination)","tags":["Automation"]}},"/v1/notifications/":{"get":{"operationId":"NotificationController.list","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Notifications retrieved successfully","data":{"items":[{"id":"c3d4e5f6-a7b8-4c3d-9e4f-5a6b7c8d9e0f","type":"PROGRESS_ENTRY","title":"New progress entry","message":"Alex Rivera logged a new weigh-in.","recipientId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","isRead":false,"priority":"NORMAL","metadata":{},"createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z"}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List notifications (paginated, 1-indexed, createdAt DESC; filters supported)","tags":["Notification"]}},"/v1/notifications/unread-count":{"get":{"operationId":"NotificationController.unreadCount","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Unread count retrieved successfully","data":{"count":2}}}},"description":"Success"}},"summary":"Get the caller’s own unread notification count","tags":["Notification"]}},"/v1/notifications/mark-all-read":{"patch":{"operationId":"NotificationController.markAllRead","parameters":[{"in":"query","name":"type","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"2 notification(s) marked as read","data":{"count":2}}}},"description":"Success"}},"summary":"Mark all notifications read (optional ?type filter); returns the flipped count","tags":["Notification"]}},"/v1/notifications/{id}":{"get":{"operationId":"NotificationController.getOne","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Notification retrieved successfully","data":{"id":"c3d4e5f6-a7b8-4c3d-9e4f-5a6b7c8d9e0f","type":"PROGRESS_ENTRY","title":"New progress entry","message":"Alex Rivera logged a new weigh-in.","recipientId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","isRead":false,"priority":"NORMAL","metadata":{},"createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z"}}}},"description":"Success"}},"summary":"Get a notification by id (404 when not found)","tags":["Notification"]},"delete":{"operationId":"NotificationController.remove","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Notification deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete a notification","tags":["Notification"]}},"/v1/notifications/{id}/mark-read":{"patch":{"operationId":"NotificationController.markRead","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/NotificationDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Notification marked as read","data":{"id":"c3d4e5f6-a7b8-4c3d-9e4f-5a6b7c8d9e0f","type":"PROGRESS_ENTRY","title":"New progress entry","message":"Alex Rivera logged a new weigh-in.","recipientId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","isRead":true,"priority":"NORMAL","metadata":{},"createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","readAt":"2026-01-15T10:00:00.000Z"}}}},"description":"Success"}},"summary":"Mark a notification as read","tags":["Notification"]}},"/v1/notifications/{id}/mark-unread":{"patch":{"operationId":"NotificationController.markUnread","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/NotificationDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Notification marked as unread","data":{"id":"c3d4e5f6-a7b8-4c3d-9e4f-5a6b7c8d9e0f","type":"PROGRESS_ENTRY","title":"New progress entry","message":"Alex Rivera logged a new weigh-in.","recipientId":"a1b2c3d4-e5f6-4a1b-8c2d-3e4f5a6b7c8d","isRead":false,"priority":"NORMAL","metadata":{},"createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z"}}}},"description":"Success"}},"summary":"Mark a notification as unread (readAt cleared → key absent under NON_NULL)","tags":["Notification"]}},"/v1/marketing/booking/trainers/{trainerId}/configuration":{"get":{"operationId":"BookingController.getBookingConfiguration","parameters":[{"in":"path","name":"trainerId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Booking configuration retrieved successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","bookingUrlSlug":"dijana-keri","sharedAvailabilities":[{"dayOfWeek":1,"startTime":"08:00:00","endTime":"20:00:00","isActive":true,"timezoneId":"America/New_York"},{"dayOfWeek":2,"startTime":"08:00:00","endTime":"20:00:00","isActive":true,"timezoneId":"America/New_York"}],"globalSettings":{"minimumAdvanceHours":4,"maximumAdvanceDays":30,"bufferMinutesBefore":0,"bufferMinutesAfter":0,"requireClientDetails":true,"autoConfirmBookings":true,"isPublicBookingEnabled":true},"eventConfigurations":[{"eventId":"30minutes","eventName":"30 Minutes","eventDescription":null,"slotDurationMinutes":30,"isActive":true,"customAvailabilities":null,"settingsOverride":null}]}}}},"description":"Success"}},"summary":"Get a trainer's booking configuration (ADMIN/TRAINER)","tags":["Booking"]},"put":{"operationId":"BookingController.updateBookingConfiguration","parameters":[{"in":"path","name":"trainerId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{},"example":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","bookingUrlSlug":"dijana-keri","sharedAvailabilities":[{"dayOfWeek":1,"startTime":"08:00:00","endTime":"20:00:00","isActive":true,"timezoneId":"America/New_York"},{"dayOfWeek":2,"startTime":"08:00:00","endTime":"20:00:00","isActive":true,"timezoneId":"America/New_York"}],"globalSettings":{"minimumAdvanceHours":4,"maximumAdvanceDays":30,"bufferMinutesBefore":0,"bufferMinutesAfter":0,"requireClientDetails":true,"autoConfirmBookings":true,"isPublicBookingEnabled":true},"eventConfigurations":[{"eventId":"30minutes","eventName":"30 Minutes","eventDescription":null,"slotDurationMinutes":30,"isActive":true,"customAvailabilities":null,"settingsOverride":null}]}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Booking configuration updated successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","bookingUrlSlug":"dijana-keri","sharedAvailabilities":[{"dayOfWeek":1,"startTime":"08:00:00","endTime":"20:00:00","isActive":true,"timezoneId":"America/New_York"},{"dayOfWeek":2,"startTime":"08:00:00","endTime":"20:00:00","isActive":true,"timezoneId":"America/New_York"}],"globalSettings":{"minimumAdvanceHours":4,"maximumAdvanceDays":30,"bufferMinutesBefore":0,"bufferMinutesAfter":0,"requireClientDetails":true,"autoConfirmBookings":true,"isPublicBookingEnabled":true},"eventConfigurations":[{"eventId":"30minutes","eventName":"30 Minutes","eventDescription":null,"slotDurationMinutes":30,"isActive":true,"customAvailabilities":null,"settingsOverride":null}]}}}},"description":"Success"}},"summary":"Update a trainer's booking configuration (ADMIN/TRAINER)","tags":["Booking"]}},"/v1/marketing/booking/public/{slug}/{eventId}/slots":{"get":{"operationId":"BookingController.getPublicAvailableSlots","parameters":[{"in":"path","name":"slug","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"path","name":"eventId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/PublicAvailableSlotDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Available slots retrieved successfully","data":[{"startTime":"2026-01-15T14:00:00.000Z","endTime":"2026-01-15T14:30:00.000Z","isAvailable":true,"totalCapacity":1,"busyCount":0,"freeCount":1}]}}},"description":"Success"}},"summary":"List available booking slots for an event (public)","tags":["Booking"]}},"/v1/marketing/booking/public/{slug}/{eventId}/book":{"post":{"operationId":"BookingController.createPublicBooking","parameters":[{"in":"path","name":"slug","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"path","name":"eventId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicBookingRequestDto"},"example":{"startTime":"2026-01-15T14:00:00.000Z","endTime":"2026-01-15T14:30:00.000Z","clientName":"John Miller","clientEmail":"john@example.com","clientPhone":"+15551234567","attendeeEmails":null,"notes":"First consultation","timezoneId":"America/New_York"}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/PublicBookingResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Booking created successfully","data":{"id":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","appointmentId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","startTime":"2026-01-15T09:00:00","endTime":"2026-01-15T09:30:00","status":"CONFIRMED","meetingTitle":"30 Minutes","trainerName":"Dijana Keri","clientName":"John Miller","clientEmail":"john@example.com","confirmationCode":"BK-7F3A9C"}}}},"description":"Success"}},"summary":"Create a public booking for an event slot","tags":["Booking"]}},"/v1/marketing/booking/public/{slug}/{eventId}":{"get":{"operationId":"BookingController.getPublicEventConfiguration","parameters":[{"in":"path","name":"slug","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"path","name":"eventId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Event configuration retrieved successfully","data":{"eventId":"30minutes","eventName":"30 Minutes","eventDescription":null,"slotDurationMinutes":30,"trainerName":"Dijana Keri","trainerMedia":null,"redirectUrl":null,"theme":null,"branding":null,"requireClientDetails":true,"additionalQuestions":null}}}},"description":"Success"}},"summary":"Get a public event configuration by slug + eventId","tags":["Booking"]}},"/v1/marketing/booking/public/{slug}":{"get":{"operationId":"BookingController.getPublicEvents","parameters":[{"in":"path","name":"slug","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Events retrieved successfully","data":[{"eventId":"30minutes","eventName":"30 Minutes","eventDescription":null,"slotDurationMinutes":30,"trainerName":"Dijana Keri","trainerMedia":null,"redirectUrl":null,"theme":null,"branding":null,"requireClientDetails":true,"additionalQuestions":null}]}}},"description":"Success"}},"summary":"List public events for a booking slug","tags":["Booking"]}},"/v1/marketing/formTemplates":{"get":{"operationId":"FormController.getFormTemplates","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/FormTemplateDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Form templates retrieved successfully","data":[{"id":"client-intake","name":"Client Intake","description":"A standard onboarding questionnaire for new clients.","category":"CLIENT_ASSESSMENT","form":{"title":"New Client Intake","description":"Tell us about your goals and history.","presentationType":"MULTI_PAGE","questions":[{"id":"q1","type":"SHORT_TEXT","title":"What is your name?","required":true},{"id":"q2","type":"SINGLE_SELECT","title":"Primary goal?","required":true,"options":[{"id":"o1","label":"Fat loss"},{"id":"o2","label":"Muscle gain"},{"id":"o3","label":"Longevity"}]}],"theme":null,"settings":{"showProgressBar":true,"notifyTrainerOnSubmit":true,"specialPurpose":"INITIAL_QUESTIONNAIRE"},"translations":null}}]}}},"description":"Success"}},"summary":"List built-in form templates (public)","tags":["Form"]}},"/v1/marketing/formTemplates/{templateId}":{"get":{"operationId":"FormController.getFormTemplateById","parameters":[{"in":"path","name":"templateId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/FormTemplateDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Form template retrieved successfully","data":{"id":"client-intake","name":"Client Intake","description":"A standard onboarding questionnaire for new clients.","category":"CLIENT_ASSESSMENT","form":{"title":"New Client Intake","description":"Tell us about your goals and history.","presentationType":"MULTI_PAGE","questions":[{"id":"q1","type":"SHORT_TEXT","title":"What is your name?","required":true},{"id":"q2","type":"SINGLE_SELECT","title":"Primary goal?","required":true,"options":[{"id":"o1","label":"Fat loss"},{"id":"o2","label":"Muscle gain"},{"id":"o3","label":"Longevity"}]}],"theme":null,"settings":{"showProgressBar":true,"notifyTrainerOnSubmit":true,"specialPurpose":"INITIAL_QUESTIONNAIRE"},"translations":null}}}}},"description":"Success"}},"summary":"Get a form template by id (public; 404 body when not found)","tags":["Form"]}},"/v1/marketing/forms":{"post":{"operationId":"FormController.createForm","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormRequestDto"},"example":{"title":"New Client Intake","description":"Tell us about your goals and history.","presentationType":"MULTI_PAGE","questions":[{"id":"q1","type":"SHORT_TEXT","title":"What is your name?","required":true},{"id":"q2","type":"SINGLE_SELECT","title":"Primary goal?","required":true,"options":[{"id":"o1","label":"Fat loss"},{"id":"o2","label":"Muscle gain"},{"id":"o3","label":"Longevity"}]}],"theme":null,"settings":{"showProgressBar":true,"notifyTrainerOnSubmit":true,"specialPurpose":"INITIAL_QUESTIONNAIRE"},"translations":null}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/FormResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Form created successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","title":"New Client Intake","description":"Tell us about your goals and history.","version":1,"presentationType":"MULTI_PAGE","questions":[{"id":"q1","type":"SHORT_TEXT","title":"What is your name?","required":true},{"id":"q2","type":"SINGLE_SELECT","title":"Primary goal?","required":true,"options":[{"id":"o1","label":"Fat loss"},{"id":"o2","label":"Muscle gain"},{"id":"o3","label":"Longevity"}]}],"theme":null,"settings":{"showProgressBar":true,"notifyTrainerOnSubmit":true,"specialPurpose":"INITIAL_QUESTIONNAIRE"},"createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","translations":null}}}},"description":"Success"}},"summary":"Create a form","tags":["Form"]},"get":{"operationId":"FormController.listForms","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Forms retrieved successfully","data":{"items":[{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","title":"New Client Intake","description":"Tell us about your goals and history.","version":1,"presentationType":"MULTI_PAGE","questions":[{"id":"q1","type":"SHORT_TEXT","title":"What is your name?","required":true},{"id":"q2","type":"SINGLE_SELECT","title":"Primary goal?","required":true,"options":[{"id":"o1","label":"Fat loss"},{"id":"o2","label":"Muscle gain"},{"id":"o3","label":"Longevity"}]}],"theme":null,"settings":{"showProgressBar":true,"notifyTrainerOnSubmit":true,"specialPurpose":"INITIAL_QUESTIONNAIRE"},"createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","translations":null}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List forms (paginated)","tags":["Form"]}},"/v1/marketing/forms/generate":{"post":{"operationId":"FormController.generateForm","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateFormRequestDto"},"example":{"currentFormSetup":{"title":"New Client Intake","description":"Tell us about your goals and history.","presentationType":"MULTI_PAGE","questions":[{"id":"q1","type":"SHORT_TEXT","title":"What is your name?","required":true},{"id":"q2","type":"SINGLE_SELECT","title":"Primary goal?","required":true,"options":[{"id":"o1","label":"Fat loss"},{"id":"o2","label":"Muscle gain"},{"id":"o3","label":"Longevity"}]}],"theme":null,"settings":{"showProgressBar":true,"notifyTrainerOnSubmit":true,"specialPurpose":"INITIAL_QUESTIONNAIRE"},"translations":null},"changeQuery":"Add a question asking about injury history."}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/GenerateFormResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Form generated successfully","data":{"updatedForm":{"title":"New Client Intake","description":"Tell us about your goals and history.","presentationType":"MULTI_PAGE","questions":[{"id":"q1","type":"SHORT_TEXT","title":"What is your name?","required":true},{"id":"q2","type":"SINGLE_SELECT","title":"Primary goal?","required":true,"options":[{"id":"o1","label":"Fat loss"},{"id":"o2","label":"Muscle gain"},{"id":"o3","label":"Longevity"}]}],"theme":null,"settings":{"showProgressBar":true,"notifyTrainerOnSubmit":true,"specialPurpose":"INITIAL_QUESTIONNAIRE"},"translations":null},"response":"Added an injury-history question to the form."}}}},"description":"Success"}},"summary":"AI-generate/modify a form from a natural-language change query","tags":["Form"]}},"/v1/marketing/forms/submit":{"post":{"operationId":"FormController.submitForm","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormSubmitRequestDto"},"example":{"formId":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","answers":[{"questionTitle":"What is your name?","answer":"John Miller","questionId":"q1"},{"questionTitle":"Primary goal?","answer":"Longevity","questionId":"q2"}],"email":"john@example.com","phone":null,"action":null,"userId":null,"submittedAt":"2026-01-15T10:00:00.000Z"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/FormSubmitResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Form submitted successfully","data":{"computedOutput":"Thanks for your submission! We will be in touch shortly.","outputFormat":"MARKDOWN"}}}},"description":"Success"}},"summary":"Submit a form (public; optional authenticated user)","tags":["Form"]}},"/v1/marketing/forms/public/{formId}":{"get":{"operationId":"FormController.getPublicFormById","parameters":[{"in":"path","name":"formId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/FormResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Form retrieved successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","title":"New Client Intake","description":"Tell us about your goals and history.","version":1,"presentationType":"MULTI_PAGE","questions":[{"id":"q1","type":"SHORT_TEXT","title":"What is your name?","required":true},{"id":"q2","type":"SINGLE_SELECT","title":"Primary goal?","required":true,"options":[{"id":"o1","label":"Fat loss"},{"id":"o2","label":"Muscle gain"},{"id":"o3","label":"Longevity"}]}],"theme":null,"settings":{"showProgressBar":true,"notifyTrainerOnSubmit":true,"specialPurpose":"INITIAL_QUESTIONNAIRE"},"createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","translations":null}}}},"description":"Success"}},"summary":"Get a form by id for public rendering (404 body when not found)","tags":["Form"]}},"/v1/marketing/forms/{formId}/submissions":{"get":{"operationId":"FormController.getFormSubmissions","parameters":[{"in":"path","name":"formId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Form submissions retrieved successfully","data":{"items":[{"id":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","formId":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","answers":[{"questionTitle":"What is your name?","answer":"John Miller","questionId":"q1"},{"questionTitle":"Primary goal?","answer":"Longevity","questionId":"q2"}],"email":"john@example.com","phone":null,"action":null,"userId":null,"leadId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z","computedOutput":null,"computedOutputFormat":null,"specialPurpose":"INITIAL_QUESTIONNAIRE"}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List submissions for a form (deprecated; paginated)","tags":["Form"]}},"/v1/marketing/forms/{formId}":{"get":{"operationId":"FormController.getFormById","parameters":[{"in":"path","name":"formId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/FormResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Form retrieved successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","title":"New Client Intake","description":"Tell us about your goals and history.","version":1,"presentationType":"MULTI_PAGE","questions":[{"id":"q1","type":"SHORT_TEXT","title":"What is your name?","required":true},{"id":"q2","type":"SINGLE_SELECT","title":"Primary goal?","required":true,"options":[{"id":"o1","label":"Fat loss"},{"id":"o2","label":"Muscle gain"},{"id":"o3","label":"Longevity"}]}],"theme":null,"settings":{"showProgressBar":true,"notifyTrainerOnSubmit":true,"specialPurpose":"INITIAL_QUESTIONNAIRE"},"createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","translations":null}}}},"description":"Success"}},"summary":"Get a form by id (404 body when not found)","tags":["Form"]},"put":{"operationId":"FormController.updateForm","parameters":[{"in":"path","name":"formId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FormUpdateRequestDto"},"example":{"title":"New Client Intake","description":"Tell us about your goals and history.","presentationType":"MULTI_PAGE","questions":[{"id":"q1","type":"SHORT_TEXT","title":"What is your name?","required":true},{"id":"q2","type":"SINGLE_SELECT","title":"Primary goal?","required":true,"options":[{"id":"o1","label":"Fat loss"},{"id":"o2","label":"Muscle gain"},{"id":"o3","label":"Longevity"}]}],"theme":null,"settings":{"showProgressBar":true,"notifyTrainerOnSubmit":true,"specialPurpose":"INITIAL_QUESTIONNAIRE"},"translations":null}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/FormResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Form updated successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","title":"New Client Intake","description":"Tell us about your goals and history.","version":1,"presentationType":"MULTI_PAGE","questions":[{"id":"q1","type":"SHORT_TEXT","title":"What is your name?","required":true},{"id":"q2","type":"SINGLE_SELECT","title":"Primary goal?","required":true,"options":[{"id":"o1","label":"Fat loss"},{"id":"o2","label":"Muscle gain"},{"id":"o3","label":"Longevity"}]}],"theme":null,"settings":{"showProgressBar":true,"notifyTrainerOnSubmit":true,"specialPurpose":"INITIAL_QUESTIONNAIRE"},"createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","translations":null}}}},"description":"Success"}},"summary":"Update a form","tags":["Form"]},"delete":{"operationId":"FormController.deleteForm","parameters":[{"in":"path","name":"formId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Form deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete a form","tags":["Form"]}},"/v1/marketing/formSubmissions/{submissionId}":{"get":{"operationId":"FormController.getFormSubmissionById","parameters":[{"in":"path","name":"submissionId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/FormSubmissionResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Form submission retrieved successfully","data":{"id":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","formId":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","answers":[{"questionTitle":"What is your name?","answer":"John Miller","questionId":"q1"},{"questionTitle":"Primary goal?","answer":"Longevity","questionId":"q2"}],"email":"john@example.com","phone":null,"action":null,"userId":null,"leadId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z","computedOutput":null,"computedOutputFormat":null,"specialPurpose":"INITIAL_QUESTIONNAIRE"}}}},"description":"Success"}},"summary":"Get a form submission by id (TRAINER/ADMIN; 404 body when not found)","tags":["Form"]}},"/v1/marketing/formSubmissions":{"get":{"operationId":"FormController.listFormSubmissions","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Form submissions retrieved successfully","data":{"items":[{"id":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","formId":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","answers":[{"questionTitle":"What is your name?","answer":"John Miller","questionId":"q1"},{"questionTitle":"Primary goal?","answer":"Longevity","questionId":"q2"}],"email":"john@example.com","phone":null,"action":null,"userId":null,"leadId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z","computedOutput":null,"computedOutputFormat":null,"specialPurpose":"INITIAL_QUESTIONNAIRE"}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List form submissions (paginated)","tags":["Form"]}},"/v1/shop/products/":{"post":{"operationId":"ProductController.createProduct","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductDto"},"example":{"name":"10-Session Personal Training Pack","isActive":true,"price":2999,"currency":"usd","pricingType":"ONE_TIME","totalSessions":10,"productType":"SESSION_PACK","fulfillmentMethod":"STRIPE_CHECKOUT"}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProductDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Product created successfully","data":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","name":"10-Session Personal Training Pack","externalId":"westride-prod-5521","isActive":true,"accessTimeLimited":false,"accessDurationDays":0,"accessDurationMonths":0,"accessDurationYears":0,"price":2999,"currency":"usd","pricingType":"ONE_TIME","syncWithStripe":true,"totalSessions":10,"billingIntervalCount":1,"productType":"SESSION_PACK","fulfillmentMethod":"STRIPE_CHECKOUT","stripeProductId":"prod_Qabc123Def456","stripePriceId":"price_1P9aBcD2eFgHiJkL"}}}},"description":"Success"}},"summary":"Create a product","tags":["Product"]},"get":{"operationId":"ProductController.listProducts","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Products retrieved successfully","data":{"items":[{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","name":"10-Session Personal Training Pack","externalId":"westride-prod-5521","isActive":true,"accessTimeLimited":false,"accessDurationDays":0,"accessDurationMonths":0,"accessDurationYears":0,"price":2999,"currency":"usd","pricingType":"ONE_TIME","syncWithStripe":true,"totalSessions":10,"billingIntervalCount":1,"productType":"SESSION_PACK","fulfillmentMethod":"STRIPE_CHECKOUT","stripeProductId":"prod_Qabc123Def456","stripePriceId":"price_1P9aBcD2eFgHiJkL"},{"id":"c3d4e5f6-a7b8-4c9d-8e0f-1a2b3c4d5e6f","createdAt":"2026-01-20T14:00:00.000Z","updatedAt":"2026-01-20T14:00:00.000Z","name":"Longevity Coaching — Monthly","isActive":true,"price":25000,"currency":"usd","pricingType":"SUBSCRIPTION","billingInterval":"MONTH","billingIntervalCount":1,"productType":"SERVICE","fulfillmentMethod":"STRIPE_CHECKOUT"}],"total":2,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List products (paginated)","tags":["Product"]}},"/v1/shop/products/external/{externalId}":{"get":{"operationId":"ProductController.getProductByExternalId","parameters":[{"in":"path","name":"externalId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Product retrieved successfully","data":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","name":"10-Session Personal Training Pack","externalId":"westride-prod-5521","isActive":true,"accessTimeLimited":false,"accessDurationDays":0,"accessDurationMonths":0,"accessDurationYears":0,"price":2999,"currency":"usd","pricingType":"ONE_TIME","syncWithStripe":true,"totalSessions":10,"billingIntervalCount":1,"productType":"SESSION_PACK","fulfillmentMethod":"STRIPE_CHECKOUT","stripeProductId":"prod_Qabc123Def456","stripePriceId":"price_1P9aBcD2eFgHiJkL"}}}},"description":"Returns a 404 body { success:false, message:\"Product not found\", data:null } when no product matches."}},"summary":"Get a product by external id","tags":["Product"],"description":"Returns a 404 body { success:false, message:\"Product not found\", data:null } when no product matches."}},"/v1/shop/products/checkout":{"post":{"operationId":"ProductController.createCheckoutSession","requestBody":{"content":{"application/json":{"schema":{},"example":{"productId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","quantity":1,"successUrl":"https://app.protocolcrm.com/checkout/success","cancelUrl":"https://app.protocolcrm.com/checkout/cancel"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{}},"description":"Successful response"},"501":{"description":"Stripe checkout is not ported in the pilot; this route always responds 501 Not Implemented.","content":{"application/json":{"example":{"success":false,"message":"Stripe checkout is not available in the pilot","data":null}}}}},"summary":"Create a Stripe checkout session (not implemented)","tags":["Product"],"description":"Stripe checkout is not ported in the pilot; this route always responds 501 Not Implemented."}},"/v1/shop/products/{productId}":{"get":{"operationId":"ProductController.getProductById","parameters":[{"in":"path","name":"productId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Product retrieved successfully","data":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","name":"10-Session Personal Training Pack","externalId":"westride-prod-5521","isActive":true,"accessTimeLimited":false,"accessDurationDays":0,"accessDurationMonths":0,"accessDurationYears":0,"price":2999,"currency":"usd","pricingType":"ONE_TIME","syncWithStripe":true,"totalSessions":10,"billingIntervalCount":1,"productType":"SESSION_PACK","fulfillmentMethod":"STRIPE_CHECKOUT","stripeProductId":"prod_Qabc123Def456","stripePriceId":"price_1P9aBcD2eFgHiJkL"}}}},"description":"Returns a 404 body { success:false, message:\"Product not found\", data:null } when no product matches."}},"summary":"Get a product by id","tags":["Product"],"description":"Returns a 404 body { success:false, message:\"Product not found\", data:null } when no product matches."},"put":{"operationId":"ProductController.updateProduct","parameters":[{"in":"path","name":"productId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductDto"},"example":{"name":"10-Session Personal Training Pack","isActive":true,"price":2999,"currency":"usd","pricingType":"ONE_TIME","totalSessions":10,"productType":"SESSION_PACK","fulfillmentMethod":"STRIPE_CHECKOUT"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProductDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Product updated successfully","data":{"id":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","name":"10-Session Personal Training Pack","externalId":"westride-prod-5521","isActive":true,"accessTimeLimited":false,"accessDurationDays":0,"accessDurationMonths":0,"accessDurationYears":0,"price":2999,"currency":"usd","pricingType":"ONE_TIME","syncWithStripe":true,"totalSessions":10,"billingIntervalCount":1,"productType":"SESSION_PACK","fulfillmentMethod":"STRIPE_CHECKOUT","stripeProductId":"prod_Qabc123Def456","stripePriceId":"price_1P9aBcD2eFgHiJkL"}}}},"description":"Success"}},"summary":"Update a product","tags":["Product"]},"delete":{"operationId":"ProductController.deleteProduct","parameters":[{"in":"path","name":"productId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Product deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete a product","tags":["Product"]}},"/v1/shop/tax-rates/":{"post":{"operationId":"TaxRateController.createTaxRate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxRateDto"},"example":{"name":"Standard VAT","rate":0.2,"isInclusive":false,"isActive":true,"syncWithStripe":true}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TaxRateDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Tax rate created successfully","data":{"id":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","name":"Standard VAT","rate":0.2,"isInclusive":false,"isActive":true,"stripeTaxRateId":"txr_1P9aBcD2eFgHiJkL","syncWithStripe":true}}}},"description":"Success"}},"summary":"Create a tax rate","tags":["Tax Rate"]},"get":{"operationId":"TaxRateController.listTaxRates","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Tax rates retrieved successfully","data":{"items":[{"id":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","name":"Standard VAT","rate":0.2,"isInclusive":false,"isActive":true,"stripeTaxRateId":"txr_1P9aBcD2eFgHiJkL","syncWithStripe":true}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List tax rates (paginated)","tags":["Tax Rate"]}},"/v1/shop/tax-rates/{taxRateId}":{"get":{"operationId":"TaxRateController.getTaxRateById","parameters":[{"in":"path","name":"taxRateId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Tax rate retrieved successfully","data":{"id":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","name":"Standard VAT","rate":0.2,"isInclusive":false,"isActive":true,"stripeTaxRateId":"txr_1P9aBcD2eFgHiJkL","syncWithStripe":true}}}},"description":"Success"}},"summary":"Get a tax rate by id","tags":["Tax Rate"]},"put":{"operationId":"TaxRateController.updateTaxRate","parameters":[{"in":"path","name":"taxRateId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaxRateDto"},"example":{"name":"Standard VAT","rate":0.2,"isInclusive":false,"isActive":true,"syncWithStripe":true}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TaxRateDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Tax rate updated successfully","data":{"id":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","name":"Standard VAT","rate":0.2,"isInclusive":false,"isActive":true,"stripeTaxRateId":"txr_1P9aBcD2eFgHiJkL","syncWithStripe":true}}}},"description":"Success"}},"summary":"Update a tax rate","tags":["Tax Rate"]},"delete":{"operationId":"TaxRateController.deleteTaxRate","parameters":[{"in":"path","name":"taxRateId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Tax rate archived successfully","data":null}}},"description":"Success"}},"summary":"Archive a tax rate","tags":["Tax Rate"]}},"/v1/shop/invoices/purchase/{purchaseId}":{"get":{"operationId":"InvoiceController.getInvoicesByPurchase","parameters":[{"in":"path","name":"purchaseId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/InvoiceDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Invoices retrieved successfully","data":[{"id":"d4e5f6a7-b8c9-4d0e-8f1a-2b3c4d5e6f70","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-01T10:00:00.000Z","purchaseId":"e5f6a7b8-c9d0-4e1f-8a2b-3c4d5e6f7081","invoiceNumber":"INV-2026-000042","amount":15000,"currency":"usd","taxAmount":2500,"status":"PAID","issuedAt":"2026-02-01T10:00:00.000Z","paidAt":"2026-02-01T10:05:00.000Z","dueAt":"2026-02-15T10:00:00.000Z","stripeInvoiceId":"in_1P9aBcD2eFgHiJkL","billingEmail":"jane.doe@example.com","billingName":"Jane Doe","lineItems":[{"name":"10-Session Personal Training Pack","quantity":1,"unitPrice":12500,"amount":12500,"taxRateName":"Standard VAT","taxRateValue":0.2,"taxInclusive":false,"taxAmount":2500}]}]}}},"description":"Success"}},"summary":"List invoices for a purchase","tags":["Invoice"]}},"/v1/shop/invoices/{invoiceId}":{"get":{"operationId":"InvoiceController.getInvoiceById","parameters":[{"in":"path","name":"invoiceId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Invoice retrieved successfully","data":{"id":"d4e5f6a7-b8c9-4d0e-8f1a-2b3c4d5e6f70","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-01T10:00:00.000Z","purchaseId":"e5f6a7b8-c9d0-4e1f-8a2b-3c4d5e6f7081","invoiceNumber":"INV-2026-000042","amount":15000,"currency":"usd","taxAmount":2500,"status":"PAID","issuedAt":"2026-02-01T10:00:00.000Z","paidAt":"2026-02-01T10:05:00.000Z","dueAt":"2026-02-15T10:00:00.000Z","stripeInvoiceId":"in_1P9aBcD2eFgHiJkL","billingEmail":"jane.doe@example.com","billingName":"Jane Doe","lineItems":[{"name":"10-Session Personal Training Pack","quantity":1,"unitPrice":12500,"amount":12500,"taxRateName":"Standard VAT","taxRateValue":0.2,"taxInclusive":false,"taxAmount":2500}]}}}},"description":"Returns a 404 body { success:false, message:\"Invoice not found\", data:null } when no invoice matches."}},"summary":"Get an invoice by id","tags":["Invoice"],"description":"Returns a 404 body { success:false, message:\"Invoice not found\", data:null } when no invoice matches."},"delete":{"operationId":"InvoiceController.deleteInvoice","parameters":[{"in":"path","name":"invoiceId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Invoice deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete an invoice","tags":["Invoice"]}},"/v1/shop/invoices/{invoiceId}/void":{"post":{"operationId":"InvoiceController.voidInvoice","parameters":[{"in":"path","name":"invoiceId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/InvoiceDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Invoice voided successfully","data":{"id":"d4e5f6a7-b8c9-4d0e-8f1a-2b3c4d5e6f70","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-01T10:00:00.000Z","purchaseId":"e5f6a7b8-c9d0-4e1f-8a2b-3c4d5e6f7081","invoiceNumber":"INV-2026-000042","amount":15000,"currency":"usd","taxAmount":2500,"status":"VOID","issuedAt":"2026-02-01T10:00:00.000Z","paidAt":null,"dueAt":"2026-02-15T10:00:00.000Z","stripeInvoiceId":"in_1P9aBcD2eFgHiJkL","billingEmail":"jane.doe@example.com","billingName":"Jane Doe","lineItems":[{"name":"10-Session Personal Training Pack","quantity":1,"unitPrice":12500,"amount":12500,"taxRateName":"Standard VAT","taxRateValue":0.2,"taxInclusive":false,"taxAmount":2500}]}}}},"description":"Success"}},"summary":"Void an invoice","tags":["Invoice"]}},"/v1/shop/configuration/":{"get":{"operationId":"ShopConfigurationController.getShopConfiguration","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ShopConfigurationDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Shop configuration retrieved successfully","data":{"id":"7c1e4b2a-6d3f-4a9e-8b21-0f5c9d2e3a4b","createdAt":"2026-01-10T08:00:00.000Z","updatedAt":"2026-02-14T16:45:00.000Z","defaultTaxRateId":"d2f5a9c1-3e7b-42a8-9c14-6b0a1d8f2e35","styling":{"logo":{"url":"https://cdn.protocolcrm.com/media/logo-peak.png"},"color":"#1F6FEB"},"businessDetails":{"businessName":"Peak Performance Coaching","address":"123 Riverside Ave, London, EC1A 1BB","vatNumber":"GB123456789","email":"billing@peakperformance.co","phone":"+44 20 7946 0958"},"invoiceFooterText":"Thank you for training with Peak Performance Coaching.","resolvedStyling":{"logo":{"url":"https://cdn.protocolcrm.com/media/logo-peak.png"},"color":"#1F6FEB","logoSource":"shop","colorSource":"shop"},"resolvedBusinessDetails":{"businessName":"Peak Performance Coaching","address":"123 Riverside Ave, London, EC1A 1BB","vatNumber":"GB123456789","email":"billing@peakperformance.co","phone":"+44 20 7946 0958"}}}}},"description":"Success"}},"summary":"Get the shop configuration for the current tenant","tags":["Shop Configuration"]},"put":{"operationId":"ShopConfigurationController.updateShopConfiguration","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShopConfigurationDto"},"example":{"defaultTaxRateId":"d2f5a9c1-3e7b-42a8-9c14-6b0a1d8f2e35","styling":{"logo":{"url":"https://cdn.protocolcrm.com/media/logo-peak.png"},"color":"#1F6FEB"},"businessDetails":{"businessName":"Peak Performance Coaching","address":"123 Riverside Ave, London, EC1A 1BB","vatNumber":"GB123456789","email":"billing@peakperformance.co","phone":"+44 20 7946 0958"},"invoiceFooterText":"Thank you for training with Peak Performance Coaching."}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ShopConfigurationDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Shop configuration saved successfully","data":{"id":"7c1e4b2a-6d3f-4a9e-8b21-0f5c9d2e3a4b","createdAt":"2026-01-10T08:00:00.000Z","updatedAt":"2026-02-14T16:45:00.000Z","defaultTaxRateId":"d2f5a9c1-3e7b-42a8-9c14-6b0a1d8f2e35","styling":{"logo":{"url":"https://cdn.protocolcrm.com/media/logo-peak.png"},"color":"#1F6FEB"},"businessDetails":{"businessName":"Peak Performance Coaching","address":"123 Riverside Ave, London, EC1A 1BB","vatNumber":"GB123456789","email":"billing@peakperformance.co","phone":"+44 20 7946 0958"},"invoiceFooterText":"Thank you for training with Peak Performance Coaching.","resolvedStyling":{"logo":{"url":"https://cdn.protocolcrm.com/media/logo-peak.png"},"color":"#1F6FEB","logoSource":"shop","colorSource":"shop"},"resolvedBusinessDetails":{"businessName":"Peak Performance Coaching","address":"123 Riverside Ave, London, EC1A 1BB","vatNumber":"GB123456789","email":"billing@peakperformance.co","phone":"+44 20 7946 0958"}}}}},"description":"Success"}},"summary":"Create or update the shop configuration","tags":["Shop Configuration"]},"delete":{"operationId":"ShopConfigurationController.deleteShopConfiguration","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Shop configuration deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete the shop configuration","tags":["Shop Configuration"]}},"/v1/team/invites":{"post":{"operationId":"TeamController.inviteTeamMember","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamInviteRequestDto"},"example":{"email":"coach@example.com","teamPermissions":["COACH"],"firstName":"Sara","lastName":"Novak"}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TeamInviteDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Invitation sent successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","email":"coach@example.com","firstName":"Sara","lastName":"Novak","teamPermissions":["COACH"],"status":"PENDING","expiresAt":"2026-01-22T09:30:00.000Z","createdAt":"2026-01-15T09:30:00.000Z","invitedByName":"Dijana Keri","isExpired":false}}}},"description":"Success"}},"summary":"Invite a team member (OWNER only)","tags":["Team"]},"get":{"operationId":"TeamController.listPendingInvites","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/TeamInviteDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Pending invitations retrieved successfully","data":[{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","email":"coach@example.com","firstName":"Sara","lastName":"Novak","teamPermissions":["COACH"],"status":"PENDING","expiresAt":"2026-01-22T09:30:00.000Z","createdAt":"2026-01-15T09:30:00.000Z","invitedByName":"Dijana Keri","isExpired":false}]}}},"description":"Success"}},"summary":"List pending invitations (OWNER or ADMIN)","tags":["Team"]}},"/v1/team/invites/token/{token}":{"get":{"operationId":"TeamController.getInviteByToken","parameters":[{"in":"path","name":"token","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TeamInviteDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Invitation retrieved successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","email":"coach@example.com","firstName":"Sara","lastName":"Novak","teamPermissions":["COACH"],"status":"PENDING","expiresAt":"2026-01-22T09:30:00.000Z","createdAt":"2026-01-15T09:30:00.000Z","invitedByName":"Dijana Keri","isExpired":false}}}},"description":"Success"}},"summary":"Get an invitation by token (public — pre-fills the registration form)","tags":["Team"]}},"/v1/team/invites/{inviteId}":{"delete":{"operationId":"TeamController.revokeInvite","parameters":[{"in":"path","name":"inviteId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Invitation revoked successfully","data":null}}},"description":"Success"}},"summary":"Revoke a pending invitation (OWNER only)","tags":["Team"]}},"/v1/team/invites/{inviteId}/resend":{"post":{"operationId":"TeamController.resendInvite","parameters":[{"in":"path","name":"inviteId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TeamInviteDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Invitation resent successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","email":"coach@example.com","firstName":"Sara","lastName":"Novak","teamPermissions":["COACH"],"status":"PENDING","expiresAt":"2026-01-22T09:30:00.000Z","createdAt":"2026-01-15T09:30:00.000Z","invitedByName":"Dijana Keri","isExpired":false}}}},"description":"Success"}},"summary":"Resend a pending invitation (OWNER only)","tags":["Team"]}},"/v1/team/members/inactive":{"get":{"operationId":"TeamController.listInactiveMembers","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/TeamMemberDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Inactive team members retrieved successfully","data":[{"id":"c3d4e5f6-a7b8-9c0d-1e2f-3a4b5c6d7e8f","email":"coach@example.com","firstName":"Sara","lastName":"Novak","teamPermissions":["COACH"],"isOwner":false,"joinedAt":"2026-01-16T12:00:00.000Z"}]}}},"description":"Success"}},"summary":"List deactivated team members (OWNER or ADMIN)","tags":["Team"]}},"/v1/team/members":{"get":{"operationId":"TeamController.listTeamMembers","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/TeamMemberDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Team members retrieved successfully","data":[{"id":"c3d4e5f6-a7b8-9c0d-1e2f-3a4b5c6d7e8f","email":"coach@example.com","firstName":"Sara","lastName":"Novak","teamPermissions":["OWNER"],"isOwner":true,"joinedAt":"2026-01-16T12:00:00.000Z"},{"id":"c3d4e5f6-a7b8-9c0d-1e2f-3a4b5c6d7e8f","email":"coach@example.com","firstName":"Sara","lastName":"Novak","teamPermissions":["COACH"],"isOwner":false,"joinedAt":"2026-01-16T12:00:00.000Z"}]}}},"description":"Success"}},"summary":"List active team members (OWNER or ADMIN)","tags":["Team"]}},"/v1/team/members/{userId}/permissions":{"put":{"operationId":"TeamController.updateMemberPermissions","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePermissionsRequestDto"},"example":{"teamPermissions":["ADMIN","COACH"]}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Permissions updated successfully","data":null}}},"description":"Success"}},"summary":"Update a team member's permissions (OWNER only)","tags":["Team"]}},"/v1/team/members/{userId}/reactivate":{"post":{"operationId":"TeamController.reactivateMember","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Team member reactivated successfully","data":null}}},"description":"Success"}},"summary":"Reactivate a deactivated team member (OWNER only)","tags":["Team"]}},"/v1/team/members/{userId}":{"delete":{"operationId":"TeamController.removeMemberFromTeam","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Team member removed successfully","data":null}}},"description":"Success"}},"summary":"Remove a team member (OWNER only)","tags":["Team"]}},"/v1/tutorial-items/aggregated":{"get":{"operationId":"TutorialItemController.getAggregatedTutorialItems","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Tutorial items retrieved successfully","data":{"items":[{"id":"c4e1b2a3-6d5f-4a7b-8c9d-0e1f2a3b4c5d","userId":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","type":"ADD_FIRST_CLIENT","message":"Add your first client to get started","isCompleted":false,"displayOrder":0,"category":"ONBOARDING","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","isTimeSensitive":false,"isExpired":false}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"Trainer-facing aggregated tutorial items (paginated)","tags":["Tutorial Item"]}},"/v1/tutorial-items/":{"get":{"operationId":"TutorialItemController.getTutorialItems","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/TutorialItemDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Tutorial items retrieved successfully","data":[{"id":"c4e1b2a3-6d5f-4a7b-8c9d-0e1f2a3b4c5d","userId":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","type":"ADD_FIRST_CLIENT","message":"Add your first client to get started","isCompleted":false,"displayOrder":0,"category":"ONBOARDING","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","isTimeSensitive":false,"isExpired":false}]}}},"description":"Success"}},"summary":"List tutorial items (optionally incompleteOnly / by userId)","tags":["Tutorial Item"]},"post":{"operationId":"TutorialItemController.createTutorialItem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TutorialItemDto"},"example":{"id":"c4e1b2a3-6d5f-4a7b-8c9d-0e1f2a3b4c5d","userId":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","type":"ADD_FIRST_CLIENT","message":"Add your first client to get started","isCompleted":false,"displayOrder":0,"category":"ONBOARDING","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","isTimeSensitive":false,"isExpired":false}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TutorialItemDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Tutorial item created successfully","data":{"id":"c4e1b2a3-6d5f-4a7b-8c9d-0e1f2a3b4c5d","userId":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","type":"ADD_FIRST_CLIENT","message":"Add your first client to get started","isCompleted":false,"displayOrder":0,"category":"ONBOARDING","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","isTimeSensitive":false,"isExpired":false}}}},"description":"Success"}},"summary":"Create a tutorial item (returns 200, not 201)","tags":["Tutorial Item"]}},"/v1/tutorial-items/{tutorialItemId}/complete":{"put":{"operationId":"TutorialItemController.markAsCompleted","parameters":[{"in":"path","name":"tutorialItemId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TutorialItemDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Tutorial item marked as completed","data":{"id":"c4e1b2a3-6d5f-4a7b-8c9d-0e1f2a3b4c5d","userId":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","type":"ADD_FIRST_CLIENT","message":"Add your first client to get started","isCompleted":true,"displayOrder":0,"category":"ONBOARDING","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","isTimeSensitive":false,"isExpired":false,"completedAt":"2026-01-15T10:00:00.000Z"}}}},"description":"Success"}},"summary":"Mark a tutorial item as completed","tags":["Tutorial Item"]}},"/v1/tutorial-items/{tutorialItemId}":{"delete":{"operationId":"TutorialItemController.deleteTutorialItem","parameters":[{"in":"path","name":"tutorialItemId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Tutorial item deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete a tutorial item","tags":["Tutorial Item"]}},"/v1/shop/purchases/":{"post":{"operationId":"PurchaseController.createPurchase","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseDto"},"example":{"userId":"a7b8c9d0-e1f2-4a3b-8c4d-5e6f70819203","productId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","name":"10-Session Personal Training Pack","status":"ACTIVE","amount":29900,"currency":"usd","quantity":1,"pricingType":"ONE_TIME","billingEmail":"jane.doe@example.com","billingName":"Jane Doe","totalSessions":10}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/PurchaseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Purchase created successfully","data":{"id":"f6a7b8c9-d0e1-4f2a-8b3c-4d5e6f708192","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-01T10:00:00.000Z","userId":"a7b8c9d0-e1f2-4a3b-8c4d-5e6f70819203","productId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","code":"PUR-7F3K9Q","name":"10-Session Personal Training Pack","status":"ACTIVE","purchasedAt":"2026-02-01T10:00:00.000Z","amount":29900,"currency":"usd","quantity":1,"pricingType":"ONE_TIME","billingEmail":"jane.doe@example.com","billingName":"Jane Doe","taxRateName":"Standard VAT","taxRateValue":0.2,"taxInclusive":false,"taxAmount":4983,"totalSessions":10,"sessionsConsumed":2,"sessionsRemaining":8,"stripePaymentIntentId":"pi_1P9aBcD2eFgHiJkL","stripeCustomerId":"cus_Qabc123Def456"}}}},"description":"Success"}},"summary":"Create a purchase","tags":["Purchase"]},"get":{"operationId":"PurchaseController.listPurchases","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Purchases retrieved successfully","data":{"items":[{"id":"f6a7b8c9-d0e1-4f2a-8b3c-4d5e6f708192","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-01T10:00:00.000Z","userId":"a7b8c9d0-e1f2-4a3b-8c4d-5e6f70819203","productId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","code":"PUR-7F3K9Q","name":"10-Session Personal Training Pack","status":"ACTIVE","purchasedAt":"2026-02-01T10:00:00.000Z","amount":29900,"currency":"usd","quantity":1,"pricingType":"ONE_TIME","billingEmail":"jane.doe@example.com","billingName":"Jane Doe","taxRateName":"Standard VAT","taxRateValue":0.2,"taxInclusive":false,"taxAmount":4983,"totalSessions":10,"sessionsConsumed":2,"sessionsRemaining":8,"stripePaymentIntentId":"pi_1P9aBcD2eFgHiJkL","stripeCustomerId":"cus_Qabc123Def456"}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List purchases (paginated)","tags":["Purchase"]}},"/v1/shop/purchases/code/{code}":{"get":{"operationId":"PurchaseController.getPurchaseByCode","parameters":[{"in":"path","name":"code","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Purchase retrieved successfully","data":{"id":"f6a7b8c9-d0e1-4f2a-8b3c-4d5e6f708192","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-01T10:00:00.000Z","userId":"a7b8c9d0-e1f2-4a3b-8c4d-5e6f70819203","productId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","code":"PUR-7F3K9Q","name":"10-Session Personal Training Pack","status":"ACTIVE","purchasedAt":"2026-02-01T10:00:00.000Z","amount":29900,"currency":"usd","quantity":1,"pricingType":"ONE_TIME","billingEmail":"jane.doe@example.com","billingName":"Jane Doe","taxRateName":"Standard VAT","taxRateValue":0.2,"taxInclusive":false,"taxAmount":4983,"totalSessions":10,"sessionsConsumed":2,"sessionsRemaining":8,"stripePaymentIntentId":"pi_1P9aBcD2eFgHiJkL","stripeCustomerId":"cus_Qabc123Def456"}}}},"description":"Returns a 404 body { success:false, message:\"Purchase not found\", data:null } when no purchase matches."}},"summary":"Get a purchase by code","tags":["Purchase"],"description":"Returns a 404 body { success:false, message:\"Purchase not found\", data:null } when no purchase matches."}},"/v1/shop/purchases/consume-session":{"post":{"operationId":"PurchaseController.consumeSession","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsumeSessionRequest"},"example":{"purchaseId":"f6a7b8c9-d0e1-4f2a-8b3c-4d5e6f708192","sessionsToConsume":1}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ConsumeSessionResponse"}},"required":["success","message","data"]},"example":{"success":true,"message":"Session consumed successfully","data":{"purchaseId":"f6a7b8c9-d0e1-4f2a-8b3c-4d5e6f708192","sessionsConsumed":3,"sessionsRemaining":7,"totalSessions":10}}}},"description":"Success"}},"summary":"Consume sessions on a purchase","tags":["Purchase"]}},"/v1/shop/purchases/spend/{sessionSpendId}":{"put":{"operationId":"PurchaseController.updateSessionSpend","parameters":[{"in":"path","name":"sessionSpendId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpendSessionRequest"},"example":{"date":"2026-02-10T16:30:00.000Z","message":"Lower-body strength session","metadata":{"location":"Studio A","coach":"Dijana"}}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/SessionSpendDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Session spend updated successfully","data":{"id":"b8c9d0e1-f2a3-4b4c-8d5e-6f7081920314","createdAt":"2026-02-10T16:30:00.000Z","updatedAt":"2026-02-10T16:30:00.000Z","purchaseId":"f6a7b8c9-d0e1-4f2a-8b3c-4d5e6f708192","spendDate":"2026-02-10T16:30:00.000Z","message":"Lower-body strength session","metadata":{"location":"Studio A","coach":"Dijana"}}}}},"description":"Success"}},"summary":"Update a session-spend record","tags":["Purchase"]},"delete":{"operationId":"PurchaseController.deleteSessionSpend","parameters":[{"in":"path","name":"sessionSpendId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Session spend deleted and session restored","data":null}}},"description":"Success"}},"summary":"Delete a session-spend record (restores the session)","tags":["Purchase"]}},"/v1/shop/purchases/{purchaseId}":{"get":{"operationId":"PurchaseController.getPurchaseById","parameters":[{"in":"path","name":"purchaseId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Purchase retrieved successfully","data":{"id":"f6a7b8c9-d0e1-4f2a-8b3c-4d5e6f708192","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-01T10:00:00.000Z","userId":"a7b8c9d0-e1f2-4a3b-8c4d-5e6f70819203","productId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","code":"PUR-7F3K9Q","name":"10-Session Personal Training Pack","status":"ACTIVE","purchasedAt":"2026-02-01T10:00:00.000Z","amount":29900,"currency":"usd","quantity":1,"pricingType":"ONE_TIME","billingEmail":"jane.doe@example.com","billingName":"Jane Doe","taxRateName":"Standard VAT","taxRateValue":0.2,"taxInclusive":false,"taxAmount":4983,"totalSessions":10,"sessionsConsumed":2,"sessionsRemaining":8,"stripePaymentIntentId":"pi_1P9aBcD2eFgHiJkL","stripeCustomerId":"cus_Qabc123Def456"}}}},"description":"Returns a 404 body { success:false, message:\"Purchase not found\", data:null } when no purchase matches."}},"summary":"Get a purchase by id","tags":["Purchase"],"description":"Returns a 404 body { success:false, message:\"Purchase not found\", data:null } when no purchase matches."},"put":{"operationId":"PurchaseController.updatePurchase","parameters":[{"in":"path","name":"purchaseId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchaseDto"},"example":{"userId":"a7b8c9d0-e1f2-4a3b-8c4d-5e6f70819203","productId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","name":"10-Session Personal Training Pack","status":"ACTIVE","amount":29900,"currency":"usd","quantity":1,"pricingType":"ONE_TIME","billingEmail":"jane.doe@example.com","billingName":"Jane Doe","totalSessions":10}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/PurchaseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Purchase updated successfully","data":{"id":"f6a7b8c9-d0e1-4f2a-8b3c-4d5e6f708192","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-01T10:00:00.000Z","userId":"a7b8c9d0-e1f2-4a3b-8c4d-5e6f70819203","productId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","code":"PUR-7F3K9Q","name":"10-Session Personal Training Pack","status":"ACTIVE","purchasedAt":"2026-02-01T10:00:00.000Z","amount":29900,"currency":"usd","quantity":1,"pricingType":"ONE_TIME","billingEmail":"jane.doe@example.com","billingName":"Jane Doe","taxRateName":"Standard VAT","taxRateValue":0.2,"taxInclusive":false,"taxAmount":4983,"totalSessions":10,"sessionsConsumed":2,"sessionsRemaining":8,"stripePaymentIntentId":"pi_1P9aBcD2eFgHiJkL","stripeCustomerId":"cus_Qabc123Def456"}}}},"description":"Success"}},"summary":"Update a purchase","tags":["Purchase"]},"delete":{"operationId":"PurchaseController.deletePurchase","parameters":[{"in":"path","name":"purchaseId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Purchase deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete a purchase","tags":["Purchase"]}},"/v1/shop/purchases/{purchaseId}/sendConfirmation":{"post":{"operationId":"PurchaseController.sendConfirmation","parameters":[{"in":"path","name":"purchaseId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Confirmation sent successfully","data":{"id":"f6a7b8c9-d0e1-4f2a-8b3c-4d5e6f708192","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-01T10:00:00.000Z","userId":"a7b8c9d0-e1f2-4a3b-8c4d-5e6f70819203","productId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","code":"PUR-7F3K9Q","name":"10-Session Personal Training Pack","status":"ACTIVE","purchasedAt":"2026-02-01T10:00:00.000Z","amount":29900,"currency":"usd","quantity":1,"pricingType":"ONE_TIME","billingEmail":"jane.doe@example.com","billingName":"Jane Doe","taxRateName":"Standard VAT","taxRateValue":0.2,"taxInclusive":false,"taxAmount":4983,"totalSessions":10,"sessionsConsumed":2,"sessionsRemaining":8,"stripePaymentIntentId":"pi_1P9aBcD2eFgHiJkL","stripeCustomerId":"cus_Qabc123Def456"}}}},"description":"Returns a 404 body { success:false, message:\"Purchase not found\", data:null } when no purchase matches."}},"summary":"Send the purchase confirmation email","tags":["Purchase"],"description":"Returns a 404 body { success:false, message:\"Purchase not found\", data:null } when no purchase matches."}},"/v1/shop/purchases/{purchaseId}/sendInvoice":{"post":{"operationId":"PurchaseController.sendInvoice","parameters":[{"in":"path","name":"purchaseId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Invoice sent successfully","data":{"id":"f6a7b8c9-d0e1-4f2a-8b3c-4d5e6f708192","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-01T10:00:00.000Z","userId":"a7b8c9d0-e1f2-4a3b-8c4d-5e6f70819203","productId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","code":"PUR-7F3K9Q","name":"10-Session Personal Training Pack","status":"ACTIVE","purchasedAt":"2026-02-01T10:00:00.000Z","amount":29900,"currency":"usd","quantity":1,"pricingType":"ONE_TIME","billingEmail":"jane.doe@example.com","billingName":"Jane Doe","taxRateName":"Standard VAT","taxRateValue":0.2,"taxInclusive":false,"taxAmount":4983,"totalSessions":10,"sessionsConsumed":2,"sessionsRemaining":8,"stripePaymentIntentId":"pi_1P9aBcD2eFgHiJkL","stripeCustomerId":"cus_Qabc123Def456"}}}},"description":"Returns a 404 body { success:false, message:\"Purchase not found\", data:null } when no purchase matches."}},"summary":"Send the purchase invoice email","tags":["Purchase"],"description":"Returns a 404 body { success:false, message:\"Purchase not found\", data:null } when no purchase matches."}},"/v1/shop/purchases/{purchaseId}/renew":{"post":{"operationId":"PurchaseController.renewPurchase","parameters":[{"in":"path","name":"purchaseId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenewPurchaseRequest"},"example":{"amount":29900}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/PurchaseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Purchase renewed successfully","data":{"id":"f6a7b8c9-d0e1-4f2a-8b3c-4d5e6f708192","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-01T10:00:00.000Z","userId":"a7b8c9d0-e1f2-4a3b-8c4d-5e6f70819203","productId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","code":"PUR-7F3K9Q","name":"10-Session Personal Training Pack","status":"ACTIVE","purchasedAt":"2026-02-01T10:00:00.000Z","amount":29900,"currency":"usd","quantity":1,"pricingType":"ONE_TIME","billingEmail":"jane.doe@example.com","billingName":"Jane Doe","taxRateName":"Standard VAT","taxRateValue":0.2,"taxInclusive":false,"taxAmount":4983,"totalSessions":10,"sessionsConsumed":2,"sessionsRemaining":8,"stripePaymentIntentId":"pi_1P9aBcD2eFgHiJkL","stripeCustomerId":"cus_Qabc123Def456"}}}},"description":"Success"}},"summary":"Renew a purchase","tags":["Purchase"]}},"/v1/shop/purchases/{purchaseId}/pause":{"post":{"operationId":"PurchaseController.pausePurchase","parameters":[{"in":"path","name":"purchaseId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PausePurchaseRequest"},"example":{"resumeAt":"2026-04-01T09:00:00.000Z"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/PurchaseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Purchase paused successfully","data":{"id":"f6a7b8c9-d0e1-4f2a-8b3c-4d5e6f708192","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-01T10:00:00.000Z","userId":"a7b8c9d0-e1f2-4a3b-8c4d-5e6f70819203","productId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","code":"PUR-7F3K9Q","name":"10-Session Personal Training Pack","status":"PAUSED","purchasedAt":"2026-02-01T10:00:00.000Z","amount":29900,"currency":"usd","quantity":1,"pricingType":"ONE_TIME","billingEmail":"jane.doe@example.com","billingName":"Jane Doe","taxRateName":"Standard VAT","taxRateValue":0.2,"taxInclusive":false,"taxAmount":4983,"totalSessions":10,"sessionsConsumed":2,"sessionsRemaining":8,"stripePaymentIntentId":"pi_1P9aBcD2eFgHiJkL","stripeCustomerId":"cus_Qabc123Def456","pausedAt":"2026-03-01T09:00:00.000Z","resumeAt":"2026-04-01T09:00:00.000Z"}}}},"description":"Success"}},"summary":"Pause a purchase","tags":["Purchase"]}},"/v1/shop/purchases/{purchaseId}/resume":{"post":{"operationId":"PurchaseController.resumePurchase","parameters":[{"in":"path","name":"purchaseId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/PurchaseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Purchase resumed successfully","data":{"id":"f6a7b8c9-d0e1-4f2a-8b3c-4d5e6f708192","createdAt":"2026-02-01T10:00:00.000Z","updatedAt":"2026-02-01T10:00:00.000Z","userId":"a7b8c9d0-e1f2-4a3b-8c4d-5e6f70819203","productId":"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","code":"PUR-7F3K9Q","name":"10-Session Personal Training Pack","status":"ACTIVE","purchasedAt":"2026-02-01T10:00:00.000Z","amount":29900,"currency":"usd","quantity":1,"pricingType":"ONE_TIME","billingEmail":"jane.doe@example.com","billingName":"Jane Doe","taxRateName":"Standard VAT","taxRateValue":0.2,"taxInclusive":false,"taxAmount":4983,"totalSessions":10,"sessionsConsumed":2,"sessionsRemaining":8,"stripePaymentIntentId":"pi_1P9aBcD2eFgHiJkL","stripeCustomerId":"cus_Qabc123Def456"}}}},"description":"Success"}},"summary":"Resume a paused purchase","tags":["Purchase"]}},"/v1/shop/purchases/{purchaseId}/spend":{"post":{"operationId":"PurchaseController.spendSession","parameters":[{"in":"path","name":"purchaseId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpendSessionRequest"},"example":{"date":"2026-02-10T16:30:00.000Z","message":"Lower-body strength session","metadata":{"location":"Studio A","coach":"Dijana"}}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/SessionSpendDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Session spent successfully","data":{"id":"b8c9d0e1-f2a3-4b4c-8d5e-6f7081920314","createdAt":"2026-02-10T16:30:00.000Z","updatedAt":"2026-02-10T16:30:00.000Z","purchaseId":"f6a7b8c9-d0e1-4f2a-8b3c-4d5e6f708192","spendDate":"2026-02-10T16:30:00.000Z","message":"Lower-body strength session","metadata":{"location":"Studio A","coach":"Dijana"}}}}},"description":"Success"}},"summary":"Spend a session on a purchase","tags":["Purchase"]}},"/v1/shop/purchases/{purchaseId}/spend-history":{"get":{"operationId":"PurchaseController.getSpendHistory","parameters":[{"in":"path","name":"purchaseId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/SessionSpendDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Spend history retrieved successfully","data":[{"id":"b8c9d0e1-f2a3-4b4c-8d5e-6f7081920314","createdAt":"2026-02-10T16:30:00.000Z","updatedAt":"2026-02-10T16:30:00.000Z","purchaseId":"f6a7b8c9-d0e1-4f2a-8b3c-4d5e6f708192","spendDate":"2026-02-10T16:30:00.000Z","message":"Lower-body strength session","metadata":{"location":"Studio A","coach":"Dijana"}}]}}},"description":"Success"}},"summary":"Get the session spend history for a purchase","tags":["Purchase"]}},"/v1/shop/overview/":{"get":{"operationId":"ShopOverviewController.getShopOverview","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ShopOverviewDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Shop overview retrieved successfully","data":{"periodData":{"startDate":"2026-06-01T00:00:00.000Z","endDate":"2026-06-30T23:59:59.000Z","totalRevenue":428500,"weeklyRevenue":[{"weekStart":"2026-06-01T00:00:00.000Z","weekEnd":"2026-06-07T23:59:59.000Z","revenue":98000},{"weekStart":"2026-06-08T00:00:00.000Z","weekEnd":"2026-06-14T23:59:59.000Z","revenue":120500}],"oneTimePurchaseCount":6,"newSubscriptionCount":3},"activeState":{"activeOneTimeCount":12,"activeSubscriptionCount":8,"expiringSoon":[{"purchaseId":"b1d4e6f8-2a3c-4d5e-9f10-1a2b3c4d5e6f","productId":"a9c8b7d6-5e4f-3a2b-1c0d-9e8f7a6b5c4d","productName":"12-Week Transformation","userId":"c3d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f","userName":"Jordan Reyes","pricingType":"ONE_TIME","expiresAt":"2026-07-10T00:00:00.000Z","amount":45000,"currency":"usd","status":"ACTIVE"}],"recentlyExpired":[{"purchaseId":"e5f6a7b8-9c0d-1e2f-3a4b-5c6d7e8f9a0b","productId":"a9c8b7d6-5e4f-3a2b-1c0d-9e8f7a6b5c4d","productName":"Monthly Coaching","userId":"f6a7b8c9-0d1e-2f3a-4b5c-6d7e8f9a0b1c","userName":"Sam Okafor","pricingType":"SUBSCRIPTION","expiresAt":"2026-06-28T00:00:00.000Z","amount":19900,"currency":"usd","status":"EXPIRED"}]},"userPurchases":{"c3d4e5f6-7a8b-9c0d-1e2f-3a4b5c6d7e8f":{"active":[],"past":[]}}}}}},"description":"Success"}},"summary":"Get the shop overview (revenue, active purchases, expiring soon) for the current tenant","tags":["Shop Overview"]}},"/v1/integrations/wordpress/webhook/woocomerce":{"post":{"operationId":"WordpressWebhookController.createMessage","parameters":[{"in":"query","name":"apiKey","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WooCommerceOrderDto"},"example":{"id":4821,"status":"completed","billing":{"first_name":"Ana","last_name":"Marković","email":"ana@example.com"},"line_items":[{"id":91,"product_id":340,"quantity":1}]}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Message received successfully","data":null}}},"description":"External WooCommerce callback. Returns a bare success envelope on a valid order; a missing body returns a bare 400 with no body."}},"summary":"WooCommerce order webhook (apiKey query auth)","tags":["Wordpress Webhook"],"description":"External WooCommerce callback. Returns a bare success envelope on a valid order; a missing body returns a bare 400 with no body."}},"/v1/integrations/wordpress/webhook/purchase":{"post":{"operationId":"WordpressWebhookController.handlePurchase","parameters":[{"in":"query","name":"apiKey","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WordpressPurchaseInfo"},"example":{"order_id":4821,"payment_reference":"ch_3PqRs7K2eZvKYlo21abcDEfg","customer_email":"ana@example.com","customer_name":"Ana Marković","products":[{"id":12,"product_id":340,"sku":"PRO-12M","name":"12-Month Coaching","quantity":1}]}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"code":"A1B2C3D4","url":"https://app.protocolcrm.com/redeem/A1B2C3D4"}}},"description":"Returns the raw { code, url } redemption object — NOT the ApiResponse envelope."}},"summary":"Register a WordPress purchase, returns a redeem code (apiKey query auth)","tags":["Wordpress Webhook"],"description":"Returns the raw { code, url } redemption object — NOT the ApiResponse envelope."}},"/v1/integrations/wordpress/validatePurchase":{"get":{"operationId":"WordpressWebhookController.validatePurchase","parameters":[{"in":"query","name":"apiKey","required":false,"schema":{"type":"string"}},{"in":"query","name":"code","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"valid":true}}},"description":"Returns the raw { valid } object — NOT the ApiResponse envelope."}},"summary":"Validate a purchase redeem code (apiKey query auth)","tags":["Wordpress Webhook"],"description":"Returns the raw { valid } object — NOT the ApiResponse envelope."}},"/v1/integrations/wordpress/resetIpAccess":{"post":{"operationId":"WordpressWebhookController.resetIpAccess","parameters":[{"in":"query","name":"code","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"IP access records reset successfully","data":null}}},"description":"Success"}},"summary":"Reset IP access records for a redeem code","tags":["Wordpress Webhook"]}},"/v1/integrations/wordpress/ipAccessList":{"get":{"operationId":"WordpressWebhookController.getIpAccessList","parameters":[{"in":"query","name":"code","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/IpAccessListResponse"}},"required":["success","message","data"]},"example":{"success":true,"message":"IP access list retrieved successfully","data":{"code":"A1B2C3D4","maxAllowed":3,"activeCount":2,"totalCount":5,"ipAccesses":[{"ipAddress":"203.0.113.42","lastAccessedAt":"2026-07-03T18:22:10.000Z","isActive":true},{"ipAddress":"198.51.100.7","lastAccessedAt":"2026-06-28T09:05:44.000Z","isActive":true}]}}}},"description":"Success"}},"summary":"List IP access records for a redeem code","tags":["Wordpress Webhook"]}},"/v1/billing/checkout":{"post":{"operationId":"PlatformBillingController.createCheckoutSession","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSubscriptionCheckoutRequest"},"example":{"additionalSeats":2,"successUrl":"https://app.protocolcrm.com/billing/success?session_id={CHECKOUT_SESSION_ID}","cancelUrl":"https://app.protocolcrm.com/billing/cancel"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/SubscriptionCheckoutResponse"}},"required":["success","message","data"]},"example":{"success":true,"message":"Checkout session created successfully","data":{"checkoutUrl":"https://checkout.stripe.com/c/pay/cs_test_a1B2c3D4e5F6g7H8i9J0","sessionId":"cs_test_a1B2c3D4e5F6g7H8i9J0"}}}},"description":"Success"}},"summary":"Create a Protocol Pro subscription checkout session","tags":["Platform Billing"]}},"/v1/billing/portal":{"post":{"operationId":"PlatformBillingController.createBillingPortalSession","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingPortalRequest"},"example":{"returnUrl":"https://app.protocolcrm.com/settings/billing"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/BillingPortalResponse"}},"required":["success","message","data"]},"example":{"success":true,"message":"Billing portal session created successfully","data":{"portalUrl":"https://billing.stripe.com/p/session/live_YWNjdF8xUWFiY1hZ"}}}},"description":"Success"}},"summary":"Create a Stripe billing portal session","tags":["Platform Billing"]}},"/v1/billing/status":{"get":{"operationId":"PlatformBillingController.getSubscriptionStatus","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/SubscriptionStatusResponse"}},"required":["success","message","data"]},"example":{"success":true,"message":"Subscription status retrieved successfully","data":{"hasActiveSubscription":true,"status":"ACTIVE","plan":"PROTOCOL_PRO","billingInterval":"MONTH","currentPeriodEnd":"2026-08-01T00:00:00.000Z","cancelAtPeriodEnd":false,"activeClientCount":24,"includedClients":20,"overageClients":4,"totalSeats":3,"includedSeats":1,"additionalSeats":2,"currentSeatUsage":3,"canAddUsers":true,"aiCreditsUsed":6200,"aiCreditsIncluded":10000,"aiCreditOverageEnabled":true,"hasUsedTrial":true,"organizationMode":"TEAM"}}}},"description":"Success"}},"summary":"Get the current subscription status, seat, client, and AI-credit usage","tags":["Platform Billing"]}},"/v1/billing/seats":{"put":{"operationId":"PlatformBillingController.updateAdditionalSeats","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAdditionalSeatsRequest"},"example":{"additionalSeats":3}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/UpdateAdditionalSeatsResponse"}},"required":["success","message","data"]},"example":{"success":true,"message":"Additional seats updated successfully","data":{"previousAdditionalSeats":2,"newAdditionalSeats":3,"totalSeats":4,"effectiveDate":"2026-07-04T00:00:00.000Z"}}}},"description":"Success"}},"summary":"Update the number of additional seats beyond the 1 included","tags":["Platform Billing"]}},"/v1/billing/usage":{"get":{"operationId":"PlatformBillingController.getUsageSummary","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/UsageSummaryResponse"}},"required":["success","message","data"]},"example":{"success":true,"message":"Usage summary retrieved successfully","data":{"activeClientCount":24,"includedClients":20,"overageClients":4,"overageClientsCost":800,"totalSeats":3,"includedSeats":1,"additionalSeats":2,"additionalSeatsCost":3800,"currentSeatUsage":3,"aiCreditsUsed":6200,"aiCreditsIncluded":10000,"overageCreditPacks":0,"overageCreditsCost":0,"aiCreditOverageEnabled":true,"baseCost":5900,"estimatedMonthlyTotal":10500,"currentPeriodEnd":"2026-08-01T00:00:00.000Z"}}}},"description":"Success"}},"summary":"Get the usage summary with estimated monthly cost breakdown (amounts in cents)","tags":["Platform Billing"]}},"/v1/billing/credits":{"get":{"operationId":"PlatformBillingController.getCreditBalance","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/CreditBalanceResponse"}},"required":["success","message","data"]},"example":{"success":true,"message":"Credit balance retrieved successfully","data":{"aiCreditsUsed":6200,"aiCreditsIncluded":10000,"overageCreditPacks":0,"estimatedOverageCost":0,"aiCreditOverageEnabled":true}}}},"description":"Success"}},"summary":"Get the AI credit balance and overage estimate (cost in cents)","tags":["Platform Billing"]}},"/v1/billing/credit-overage":{"put":{"operationId":"PlatformBillingController.toggleCreditOverage","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditOverageToggleRequest"},"example":{"enabled":true}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/CreditOverageToggleResponse"}},"required":["success","message","data"]},"example":{"success":true,"message":"Credit overage setting updated successfully","data":{"aiCreditOverageEnabled":true}}}},"description":"Success"}},"summary":"Enable or disable AI credit overage billing","tags":["Platform Billing"]}},"/v1/billing/webhook/":{"post":{"operationId":"PlatformBillingWebhookController.handle","parameters":[{"in":"header","name":"Stripe-Signature","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Webhook event processed successfully","data":"OK"}}},"description":"Public endpoint authenticated by Stripe signature verification (not a JWT). The body is a raw, signed Stripe event payload (checkout.session.completed, invoice.paid, customer.subscription.*, etc.), so it has no documented request schema. Returns 200 on success, 400 on bad signature, 500 on a processing error (so Stripe retries), 503 when Stripe is not configured."}},"summary":"Stripe platform-billing webhook receiver","tags":["Platform Billing Webhook"],"description":"Public endpoint authenticated by Stripe signature verification (not a JWT). The body is a raw, signed Stripe event payload (checkout.session.completed, invoice.paid, customer.subscription.*, etc.), so it has no documented request schema. Returns 200 on success, 400 on bad signature, 500 on a processing error (so Stripe retries), 503 when Stripe is not configured."}},"/v1/shop/configuration/stripe/":{"get":{"operationId":"StripeConfigurationController.get","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Stripe configuration retrieved successfully","data":{"id":"5a2b8c1d-4e6f-7a90-b1c2-3d4e5f6a7b80","publishableKey":"pk_live_51QabcXY2eZvKYlo2ExampleKeyValue00","secretKey":null,"hasSecretKey":true,"webhookSecret":null,"hasWebhookSecret":true,"createdAt":"2026-01-20T10:15:00.000Z","updatedAt":"2026-03-05T14:22:00.000Z"}}}},"description":"Success"}},"summary":"Get the Stripe configuration for the current tenant (secrets redacted)","tags":["Stripe Configuration"]},"put":{"operationId":"StripeConfigurationController.update","requestBody":{"content":{"application/json":{"schema":{},"example":{"publishableKey":"pk_live_51QabcXY2eZvKYlo2ExampleKeyValue00","secretKey":"sk_live_51QabcXY2eZvKYlo2ExampleSecret000"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Stripe configuration saved successfully","data":{"id":"5a2b8c1d-4e6f-7a90-b1c2-3d4e5f6a7b80","publishableKey":"pk_live_51QabcXY2eZvKYlo2ExampleKeyValue00","secretKey":null,"hasSecretKey":true,"webhookSecret":null,"hasWebhookSecret":true,"createdAt":"2026-01-20T10:15:00.000Z","updatedAt":"2026-03-05T14:22:00.000Z"}}}},"description":"Success"}},"summary":"Create or update the Stripe configuration (validates the secret key against Stripe)","tags":["Stripe Configuration"]},"delete":{"operationId":"StripeConfigurationController.delete","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Stripe configuration deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete the Stripe configuration (best-effort removes the remote webhook endpoint)","tags":["Stripe Configuration"]}},"/v1/shop/configuration/stripe/test":{"post":{"operationId":"StripeConfigurationController.test","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Stripe connection successful","data":{"success":true,"message":"Stripe connection successful","accountId":"acct_1QabcXY2eZvKYlo2","accountName":"Peak Performance Coaching"}}}},"description":"Success"}},"summary":"Test the stored Stripe credentials (calls Stripe accounts.retrieve)","tags":["Stripe Configuration"]}},"/v1/shop/mobile-iap/ios/validate":{"post":{"operationId":"MobileIapController.validateIos","requestBody":{"content":{"application/json":{"schema":{},"example":{"receiptData":"MIISkQYJKoZIhvcNAQcCoIISgjCCEn4CAQExCzAJBgUrDgMCGgUAMIICMwYJKoZIhv...=="}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"iOS receipt validated successfully (purchase persistence pending)","data":{"environment":"Production","persisted":false}}}},"description":"Success"}},"summary":"Validate an iOS App Store IAP receipt via Apple verifyReceipt","tags":["Mobile Iap"]}},"/v1/shop/mobile-iap/android/validate":{"post":{"operationId":"MobileIapController.validateAndroid","requestBody":{"content":{"application/json":{"schema":{},"example":{"productId":"com.protocol.coaching.monthly","purchaseToken":"hlmnpabcdefghijklmnop.AO-J1Ox0Example-PurchaseToken-Value-1234567890"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{}},"description":"Successful response"},"501":{"description":"Android receipt validation requires per-tenant Google Play service-account credentials that are not yet wired in api; this route always returns 501.","content":{"application/json":{"example":{"success":false,"message":"Android IAP receipt validation requires Google Play service-account credentials per tenant — not yet wired in api.","data":null}}}}},"summary":"Validate an Android Google Play IAP receipt (not yet implemented — returns 501)","tags":["Mobile Iap"],"description":"Android receipt validation requires per-tenant Google Play service-account credentials that are not yet wired in api; this route always returns 501."}},"/v1/shop/mobile-iap/products":{"get":{"operationId":"MobileIapController.products","parameters":[{"in":"query","name":"platform","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"IAP products retrieved successfully","data":[]}}},"description":"Success"}},"summary":"List IAP products for the given platform (currently returns an empty list)","tags":["Mobile Iap"]}},"/v1/users/{userId}/profile/health":{"get":{"operationId":"UserProfileController.getHealth","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/UserHealthProfileResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Health profile retrieved successfully","data":{"dateOfBirth":"1990-05-01","gender":"MALE","heightCm":180,"notes":"Generally fit","allergies":["pollen"],"clientDeniesPreviousInjuries":true,"riskAssessment":"HIGH","medicalConditions":[{"id":"a1111111-1111-4111-8111-111111111111","name":"Asthma","diagnosedDate":null,"notes":null,"medications":[],"riskLevel":"LOW"}],"assessmentResults":[{"id":"a2222222-2222-4222-8222-222222222222","assessmentType":"STRENGTH","date":"2024-02-02","score":42,"unit":null,"notes":null,"percentile":null}]}}}},"description":"Success"}},"summary":"Get a user health profile (lazy-created)","tags":["User Profile"]},"put":{"operationId":"UserProfileController.updateHealth","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserHealthProfileRequestDto"},"example":{"dateOfBirth":"1990-05-01","gender":"MALE","heightCm":180,"notes":"Generally fit","allergies":["pollen"],"riskAssessment":"HIGH","clientDeniesPreviousInjuries":true,"medicalConditions":[{"name":"Asthma","riskLevel":"LOW"}]}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/UserHealthProfileResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Health profile updated successfully","data":{"dateOfBirth":"1990-05-01","gender":"MALE","heightCm":180,"notes":"Generally fit","allergies":["pollen"],"clientDeniesPreviousInjuries":true,"riskAssessment":"HIGH","medicalConditions":[{"id":"a1111111-1111-4111-8111-111111111111","name":"Asthma","diagnosedDate":null,"notes":null,"medications":[],"riskLevel":"LOW"}],"assessmentResults":[{"id":"a2222222-2222-4222-8222-222222222222","assessmentType":"STRENGTH","date":"2024-02-02","score":42,"unit":null,"notes":null,"percentile":null}]}}}},"description":"Success"}},"summary":"Update a user health profile (partial)","tags":["User Profile"]}},"/v1/users/{userId}/profile/fitness":{"get":{"operationId":"UserProfileController.getFitness","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/UserFitnessProfileDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Fitness profile retrieved successfully","data":{"experienceLevel":"INTERMEDIATE","primaryGoals":["STRENGTH"],"preferredTrainingStyles":["HIIT"],"availableDays":[1,2,4],"maxSessionsPerWeek":4,"sessionDurationPreferenceMinutes":60,"equipmentAccessLevel":["DUMBBELL"],"notes":"Prefers morning sessions"}}}},"description":"Success"}},"summary":"Get a user fitness profile (lazy-created)","tags":["User Profile"]},"put":{"operationId":"UserProfileController.updateFitness","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserFitnessProfileDto"},"example":{"experienceLevel":"INTERMEDIATE","primaryGoals":["STRENGTH"],"preferredTrainingStyles":["HIIT"],"availableDays":[1,2,4],"maxSessionsPerWeek":4,"sessionDurationPreferenceMinutes":60,"equipmentAccessLevel":["DUMBBELL"],"notes":"Prefers morning sessions"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/UserFitnessProfileDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Fitness profile updated successfully","data":{"experienceLevel":"INTERMEDIATE","primaryGoals":["STRENGTH"],"preferredTrainingStyles":["HIIT"],"availableDays":[1,2,4],"maxSessionsPerWeek":4,"sessionDurationPreferenceMinutes":60,"equipmentAccessLevel":["DUMBBELL"],"notes":"Prefers morning sessions"}}}},"description":"Success"}},"summary":"Update a user fitness profile (partial)","tags":["User Profile"]}},"/v1/users/{userId}/profile/behavioral":{"get":{"operationId":"UserProfileController.getBehavioral","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/UserBehavioralProfileResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Behavioral profile retrieved successfully","data":{"userId":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","motivationFactors":["HEALTH"],"adherenceRate":80,"preferredCommunicationFrequency":"WEEKLY","behavioralTriggers":{"stress":"high"}}}}},"description":"Success"}},"summary":"Get a user behavioral profile (lazy-created)","tags":["User Profile"]},"put":{"operationId":"UserProfileController.updateBehavioral","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserBehavioralProfileRequestDto"},"example":{"motivationFactors":["HEALTH"],"adherenceRate":80,"preferredCommunicationFrequency":"WEEKLY","behavioralTriggers":{"stress":"high"}}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/UserBehavioralProfileResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Behavioral profile updated successfully","data":{"userId":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","motivationFactors":["HEALTH"],"adherenceRate":80,"preferredCommunicationFrequency":"WEEKLY","behavioralTriggers":{"stress":"high"}}}}},"description":"Success"}},"summary":"Update a user behavioral profile (partial)","tags":["User Profile"]}},"/v1/users/{userId}/profile/biometrics":{"get":{"operationId":"UserProfileController.getBiometrics","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"query","name":"timeRangeInDays","required":false,"schema":{}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/BiometricProfileResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Biometric profile retrieved successfully","data":{"weight":{"latest":80,"average":80,"min":80,"max":80,"unit":"kg","dataPoints":[{"value":80,"measureDate":"2026-01-15"}]},"lastUpdatedAt":"2026-01-15T09:30:00.000Z"}}}},"description":"Success"}},"summary":"Get a user biometric profile (windowed by ?timeRangeInDays, default 30)","tags":["User Profile"]}},"/v1/users/{userId}/profile/biometrics/data-points":{"put":{"operationId":"UserProfileController.updateBiometricDataPoints","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BiometricDataPointsUpdateRequestDto"},"example":{"dataPoints":[{"metricType":"WEIGHT","value":80,"measureDate":"2026-01-15","measureTime":"08:30","sourceDevice":"scale"}]}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/BiometricProfileResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Biometric data points updated successfully","data":{"weight":{"latest":80,"average":80,"min":80,"max":80,"unit":"kg","dataPoints":[{"value":80,"measureDate":"2026-01-15"}]},"lastUpdatedAt":"2026-01-15T09:30:00.000Z"}}}},"description":"Success"}},"summary":"Upsert biometric data points (creates the profile + summary)","tags":["User Profile"]}},"/v1/users/{userId}/profile/genomic":{"get":{"operationId":"UserProfileController.getGenomic","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/GenomicProfileResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Genomic profile retrieved successfully","data":{"id":"c4444444-4444-4444-8444-444444444444","overallAssessment":"No significant risk markers found","genomicInsights":[{"id":"b3333333-3333-4333-8333-333333333333","conditionName":"Caffeine metabolism","category":"Metabolism","resultState":"ENHANCED","mainMessage":"Fast metabolizer"}],"lastUpdatedAt":"2026-01-15T09:30:00.000Z"}}}},"description":"Success"}},"summary":"Get a user genomic profile","tags":["User Profile"]},"put":{"operationId":"UserProfileController.updateGenomic","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenomicProfileRequestDto"},"example":{"overallAssessment":"No significant risk markers found","genomicInsights":[{"conditionName":"Lactose intolerance","category":"Diet","resultState":"NORMAL","mainMessage":"Normal lactase activity"}]}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/GenomicProfileResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Genomic profile updated successfully","data":{"id":"c4444444-4444-4444-8444-444444444444","overallAssessment":"No significant risk markers found","genomicInsights":[{"id":"b3333333-3333-4333-8333-333333333333","conditionName":"Caffeine metabolism","category":"Metabolism","resultState":"ENHANCED","mainMessage":"Fast metabolizer"}],"lastUpdatedAt":"2026-01-15T09:30:00.000Z"}}}},"description":"Success"}},"summary":"Update a user genomic profile (replaces insights)","tags":["User Profile"]}},"/v1/users/{userId}/profile/genomic/insights":{"post":{"operationId":"UserProfileController.addGenomicInsights","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"items":{"type":"object","$ref":"#/components/schemas/GenomicInsightRequestDto"},"type":"array"},"example":[{"conditionName":"Caffeine metabolism","category":"Metabolism","resultState":"ENHANCED","mainMessage":"Fast metabolizer"}]}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/GenomicInsightResponseDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Genomic insight added successfully","data":[{"id":"b3333333-3333-4333-8333-333333333333","conditionName":"Caffeine metabolism","category":"Metabolism","resultState":"ENHANCED","mainMessage":"Fast metabolizer"}]}}},"description":"Success"}},"summary":"Append genomic insights (lazy-creates the profile)","tags":["User Profile"]}},"/v1/users/{userId}/profile/genomic/insights/{insightId}":{"get":{"operationId":"UserProfileController.getGenomicInsight","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"path","name":"insightId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/GenomicInsightResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Genomic insight retrieved successfully","data":{"id":"b3333333-3333-4333-8333-333333333333","conditionName":"Caffeine metabolism","category":"Metabolism","resultState":"ENHANCED","mainMessage":"Fast metabolizer"}}}},"description":"Success"}},"summary":"Get a single genomic insight","tags":["User Profile"]},"delete":{"operationId":"UserProfileController.deleteGenomicInsight","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"path","name":"insightId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Genomic insight deleted successfully","data":true}}},"description":"Success"}},"summary":"Delete a genomic insight (200 {data:true} / 404 {data:false})","tags":["User Profile"]}},"/v1/users/{userId}/profile/nutrition":{"get":{"operationId":"UserProfileController.getNutrition","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/UserNutritionProfileDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Nutrition profile retrieved successfully","data":{"primaryDietType":"KETO","nutritionGoals":["WEIGHT_LOSS"],"foodPreferences":[{"foodCategory":"PROTEIN","preferenceLevel":"LIKE","specificFoods":["chicken"]}],"dietaryRestrictions":[{"food":"peanuts","isAllergy":true,"severity":3}],"notes":"No nuts"}}}},"description":"Success"}},"summary":"Get a user nutrition profile (lazy-created)","tags":["User Profile"]},"put":{"operationId":"UserProfileController.updateNutrition","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserNutritionProfileDto"},"example":{"primaryDietType":"KETO","nutritionGoals":["WEIGHT_LOSS"],"foodPreferences":[{"foodCategory":"PROTEIN","preferenceLevel":"LIKE","specificFoods":["chicken"]}],"dietaryRestrictions":[{"food":"peanuts","isAllergy":true,"severity":3}],"notes":"No nuts"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/UserNutritionProfileDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Nutrition profile updated successfully","data":{"primaryDietType":"KETO","nutritionGoals":["WEIGHT_LOSS"],"foodPreferences":[{"foodCategory":"PROTEIN","preferenceLevel":"LIKE","specificFoods":["chicken"]}],"dietaryRestrictions":[{"food":"peanuts","isAllergy":true,"severity":3}],"notes":"No nuts"}}}},"description":"Success"}},"summary":"Update a user nutrition profile (partial)","tags":["User Profile"]}},"/v1/users/{userId}/profiles/incomplete":{"get":{"operationId":"UserProfileController.getIncompleteProfiles","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/IncompleteProfilesResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Incomplete profiles retrieved successfully","data":{"incompleteProfiles":[{"profileCode":"health","incompleteFields":["gender","heightCm"],"completionPercentage":40},{"profileCode":"fitness","incompleteFields":[],"completionPercentage":100},{"profileCode":"nutrition","incompleteFields":[],"completionPercentage":100},{"profileCode":"behavioral","incompleteFields":["motivationFactors"],"completionPercentage":60},{"profileCode":"biometrics","incompleteFields":["weight"],"completionPercentage":0},{"profileCode":"blood","incompleteFields":["panel"],"completionPercentage":0},{"profileCode":"genomic","incompleteFields":["insights"],"completionPercentage":0}]}}}},"description":"Success"}},"summary":"List incomplete profiles with completion percentages","tags":["User Profile"]}},"/v1/users/{userId}/onboarding":{"post":{"operationId":"UserProfileController.processOnboarding","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingRequestDto"},"example":{"healthProfile":{"gender":"MALE","heightCm":175,"dateOfBirth":"1992-01-01","clientDeniesAllergies":true,"clientDeniesPreviousInjuries":true,"clientDeniesMedicalConditions":true},"fitnessProfile":{"experienceLevel":"BEGINNER","primaryGoals":["STRENGTH"]}}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/IncompleteProfilesResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Onboarding data processed successfully","data":{"incompleteProfiles":[{"profileCode":"health","incompleteFields":["gender","heightCm"],"completionPercentage":40},{"profileCode":"fitness","incompleteFields":[],"completionPercentage":100},{"profileCode":"nutrition","incompleteFields":[],"completionPercentage":100},{"profileCode":"behavioral","incompleteFields":["motivationFactors"],"completionPercentage":60},{"profileCode":"biometrics","incompleteFields":["weight"],"completionPercentage":0},{"profileCode":"blood","incompleteFields":["panel"],"completionPercentage":0},{"profileCode":"genomic","incompleteFields":["insights"],"completionPercentage":0}]}}}},"description":"Success"}},"summary":"Apply onboarding sub-profiles then re-report completion","tags":["User Profile"]}},"/v1/users/{userId}/genomic/ai-extract":{"post":{"operationId":"UserProfileController.extractGenomicInsights","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{},"example":{"textContent":"Weight 80kg measured this morning","files":[]}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Genomic insights extracted successfully","data":{"dataPoints":[{"conditionName":"Caffeine metabolism","category":"Metabolism","resultState":"ENHANCED","mainMessage":"Fast metabolizer"}],"success":true}}}},"description":"Success"}},"summary":"Extract genomic insights from documents/text (synchronous)","tags":["User Profile"]},"get":{"operationId":"UserProfileController.getLatestGenomicExtract","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"No async extraction task (synchronous mode)","data":null}}},"description":"Success"}},"summary":"Latest genomic extract task (sync mode: always data:null)","tags":["User Profile"]},"delete":{"operationId":"UserProfileController.deleteGenomicExtract","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Success","data":null}}},"description":"Success"}},"summary":"Delete genomic extract task (sync mode: no-op)","tags":["User Profile"]}},"/v1/users/{userId}/biometrics/ai-extract":{"post":{"operationId":"UserProfileController.extractBiometricInsights","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{},"example":{"textContent":"Weight 80kg measured this morning","files":[]}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Biometric insights extracted successfully","data":{"dataPoints":[{"metricType":"WEIGHT","value":80,"measureDate":"2026-01-15","measureTime":"08:00"}],"success":true}}}},"description":"Success"}},"summary":"Extract biometric data points from documents/text (synchronous)","tags":["User Profile"]},"get":{"operationId":"UserProfileController.getLatestBiometricExtract","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"No async extraction task (synchronous mode)","data":null}}},"description":"Success"}},"summary":"Latest biometric extract task (sync mode: always data:null)","tags":["User Profile"]},"delete":{"operationId":"UserProfileController.deleteBiometricExtract","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Success","data":null}}},"description":"Success"}},"summary":"Delete biometric extract task (sync mode: no-op)","tags":["User Profile"]}},"/v1/health/sync":{"post":{"operationId":"HealthController.sync","requestBody":{"content":{"application/json":{"schema":{},"example":{"source":"healthkit","points":[{"metric":"steps","value":10342,"localDate":"2026-07-03"},{"metric":"weight","value":81.5,"localDate":"2026-07-03","sourceDevice":"Apple Watch Series 9"},{"metric":"hrv","value":58,"localDate":"2026-07-03"}],"sessions":[{"kind":"sleep","externalId":"hc-sleep-2026-07-03","startAt":"2026-07-02T22:45:00.000Z","endAt":"2026-07-03T06:30:00.000Z","localDate":"2026-07-03","detail":{"deepMin":92,"remMin":118,"lightMin":255,"efficiencyPct":91,"score":84}}]}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Health data synced successfully","data":{"acceptedPoints":3,"upsertedPoints":3,"acceptedSessions":1,"upsertedSessions":1}}}},"description":"Success"}},"summary":"Sync health points and sessions (idempotent batch upsert)","tags":["Health"]}},"/v1/health/metrics":{"get":{"operationId":"HealthController.getMetrics","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Health metrics retrieved successfully","data":{"userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","from":"2026-06-04","to":"2026-07-03","series":[{"metric":"hrv","unit":"ms","latest":58,"average":54,"min":45,"max":62,"optimalMin":45,"optimalMax":95,"points":[{"localDate":"2026-07-03","value":58,"source":"whoop","sourceDevice":"WHOOP 4.0"},{"localDate":"2026-07-02","value":51,"source":"whoop","sourceDevice":"WHOOP 4.0"}]}]}}}},"description":"Success"}},"summary":"Windowed metric series with stats and optimal ranges","tags":["Health"]}},"/v1/health/sources":{"get":{"operationId":"HealthController.getSources","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Health sources retrieved successfully","data":[{"source":"healthkit","status":"connected","lastSyncAt":"2026-07-03T06:35:12.000Z"},{"source":"whoop","status":"connected","lastSyncAt":"2026-07-03T05:12:00.000Z"}]}}},"description":"Success"}},"summary":"List connected health sources with last-sync times","tags":["Health"]}},"/v1/health/labs/ai-extract":{"post":{"operationId":"LabsController.aiExtract","requestBody":{"content":{"application/json":{"schema":{},"example":{"files":[{"url":"https://media.protocolcrm.com/labs/quest-2026-05-28.pdf"}]}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Lab results extracted successfully","data":{"results":[{"name":"Hemoglobin A1c","code":"HBA1C","value":"5.4","numericValue":5.4,"unit":"%","referenceRange":"4–5.6","flag":"NORMAL","metric":"hba1c"},{"name":"Vitamin D, 25-OH","value":"48","numericValue":48,"unit":"ng/mL","referenceRange":"30–100","flag":"NORMAL","metric":"vitamin_d"}],"localDate":"2026-05-28","labName":"Quest Diagnostics","processingNotes":null}}}},"description":"Success"}},"summary":"Extract lab results from documents/text (nothing persisted)","tags":["Labs"]}},"/v1/health/labs/":{"post":{"operationId":"LabsController.create","requestBody":{"content":{"application/json":{"schema":{},"example":{"localDate":"2026-06-01","drawnAt":"2026-06-01T07:30:00.000Z","labName":"LabCorp","results":[{"name":"LDL Cholesterol","code":"LDL-C","value":"102","numericValue":102,"unit":"mg/dL","referenceRange":"0–130"},{"name":"HDL Cholesterol","code":"HDL-C","value":"58","numericValue":58,"unit":"mg/dL"},{"name":"Fasting Glucose","value":"92","numericValue":92,"unit":"mg/dL"}],"notes":"Fasted 12h before the draw."}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"example":{"success":true,"message":"Lab report created successfully","data":{"id":"7c1e9a2b-4d6f-4a8e-9b0c-1d2e3f4a5b6c","userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","drawnAt":"2026-06-01T07:30:00.000Z","localDate":"2026-06-01","labName":"LabCorp","mediaId":null,"results":[{"name":"LDL Cholesterol","code":"LDL-C","value":"102","numericValue":102,"unit":"mg/dL","referenceRange":"0–130","flag":"NORMAL","metric":"ldl_c"},{"name":"HDL Cholesterol","code":"HDL-C","value":"58","numericValue":58,"unit":"mg/dL","referenceRange":"40–90","flag":"NORMAL","metric":"hdl_c"},{"name":"Fasting Glucose","value":"92","numericValue":92,"unit":"mg/dL","referenceRange":"70–99","flag":"NORMAL","metric":"fasting_glucose"},{"name":"Rheumatoid Factor","value":"negative","metric":null}],"notes":"Fasted 12h before the draw.","createdAt":"2026-06-01T14:10:05.000Z","updatedAt":"2026-06-01T14:10:05.000Z"}}}},"description":"Success"}},"summary":"Create a lab report (derives curated trend points)","tags":["Labs"]},"get":{"operationId":"LabsController.list","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Lab reports retrieved successfully","data":[{"id":"7c1e9a2b-4d6f-4a8e-9b0c-1d2e3f4a5b6c","userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","drawnAt":"2026-06-01T07:30:00.000Z","localDate":"2026-06-01","labName":"LabCorp","mediaId":null,"results":[{"name":"LDL Cholesterol","code":"LDL-C","value":"102","numericValue":102,"unit":"mg/dL","referenceRange":"0–130","flag":"NORMAL","metric":"ldl_c"},{"name":"HDL Cholesterol","code":"HDL-C","value":"58","numericValue":58,"unit":"mg/dL","referenceRange":"40–90","flag":"NORMAL","metric":"hdl_c"},{"name":"Fasting Glucose","value":"92","numericValue":92,"unit":"mg/dL","referenceRange":"70–99","flag":"NORMAL","metric":"fasting_glucose"},{"name":"Rheumatoid Factor","value":"negative","metric":null}],"notes":"Fasted 12h before the draw.","createdAt":"2026-06-01T14:10:05.000Z","updatedAt":"2026-06-01T14:10:05.000Z"}]}}},"description":"Success"}},"summary":"List lab reports (newest first)","tags":["Labs"]}},"/v1/health/labs/{reportId}":{"get":{"operationId":"LabsController.getById","parameters":[{"in":"path","name":"reportId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Lab report retrieved successfully","data":{"id":"7c1e9a2b-4d6f-4a8e-9b0c-1d2e3f4a5b6c","userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","drawnAt":"2026-06-01T07:30:00.000Z","localDate":"2026-06-01","labName":"LabCorp","mediaId":null,"results":[{"name":"LDL Cholesterol","code":"LDL-C","value":"102","numericValue":102,"unit":"mg/dL","referenceRange":"0–130","flag":"NORMAL","metric":"ldl_c"},{"name":"HDL Cholesterol","code":"HDL-C","value":"58","numericValue":58,"unit":"mg/dL","referenceRange":"40–90","flag":"NORMAL","metric":"hdl_c"},{"name":"Fasting Glucose","value":"92","numericValue":92,"unit":"mg/dL","referenceRange":"70–99","flag":"NORMAL","metric":"fasting_glucose"},{"name":"Rheumatoid Factor","value":"negative","metric":null}],"notes":"Fasted 12h before the draw.","createdAt":"2026-06-01T14:10:05.000Z","updatedAt":"2026-06-01T14:10:05.000Z"}}}},"description":"Success"}},"summary":"Get a lab report by id","tags":["Labs"]},"delete":{"operationId":"LabsController.remove","parameters":[{"in":"path","name":"reportId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Lab report deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete a lab report and its derived points","tags":["Labs"]}},"/v1/integrations/whoop/connect":{"get":{"operationId":"WhoopController.connect","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Whoop consent URL created","data":{"url":"https://api.prod.whoop.com/oauth/oauth2/auth?client_id=abc123&response_type=code&scope=read%3Arecovery%20read%3Asleep%20offline&redirect_uri=https%3A%2F%2Fapi.protocolcrm.com%2Fv1%2Fintegrations%2Fwhoop%2Fcallback&state=eyJ...signed"}}}},"description":"Success"}},"summary":"Build a Whoop OAuth consent URL with signed state","tags":["Whoop"]}},"/v1/integrations/whoop/callback":{"get":{"operationId":"WhoopController.callback","responses":{"200":{"content":{"application/json":{}},"description":"Whoop redirects the user browser here after consent. Exchanges the code, upserts the integration, then either 302-redirects into the mobile app scheme or returns a plain HTML success page. Not a JSON endpoint."}},"summary":"Whoop OAuth callback (public)","tags":["Whoop"],"description":"Whoop redirects the user browser here after consent. Exchanges the code, upserts the integration, then either 302-redirects into the mobile app scheme or returns a plain HTML success page. Not a JSON endpoint."}},"/v1/integrations/whoop/webhook":{"post":{"operationId":"WhoopController.webhook","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Processed","data":null}}},"description":"Receives signed Whoop events (sleep/recovery/workout). Auth is the X-WHOOP-Signature HMAC of timestamp+rawBody, not a JWT. Returns a raw ack; a processing failure returns 500 so Whoop retries."}},"summary":"Whoop webhook (HMAC signature-verified, public)","tags":["Whoop"],"description":"Receives signed Whoop events (sleep/recovery/workout). Auth is the X-WHOOP-Signature HMAC of timestamp+rawBody, not a JWT. Returns a raw ack; a processing failure returns 500 so Whoop retries."}},"/v1/integrations/whoop/":{"delete":{"operationId":"WhoopController.disconnect","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Whoop disconnected","data":null}}},"description":"Success"}},"summary":"Disconnect Whoop (revoke tokens locally)","tags":["Whoop"]}},"/v1/trainer-assignments/":{"post":{"operationId":"TrainerAssignmentController.create","requestBody":{"content":{"application/json":{"schema":{},"example":{"trainerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","userId":"2b3c4d5e-6f70-8a91-b2c3-d4e5f6071829","notes":"Onboarded via referral."}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"example":{"success":true,"message":"Assignment created successfully","data":{"id":"c4d5e6f7-0819-4a2b-8c3d-4e5f60718293","trainerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","userId":"2b3c4d5e-6f70-8a91-b2c3-d4e5f6071829","startDate":"2026-01-15T09:30:00.000Z","endDate":null,"status":"active","notes":"Onboarded via referral."}}}},"description":"Success"}},"summary":"Create a trainer/client assignment (idempotent on an active pair)","tags":["Trainer Assignment"]},"get":{"operationId":"TrainerAssignmentController.list","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Assignments retrieved successfully","data":[{"id":"c4d5e6f7-0819-4a2b-8c3d-4e5f60718293","trainerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","userId":"2b3c4d5e-6f70-8a91-b2c3-d4e5f6071829","startDate":"2026-01-15T09:30:00.000Z","endDate":null,"status":"active","notes":"Onboarded via referral."}]}}},"description":"Success"}},"summary":"List trainer/client assignments (scoped by role; filter by userIds/status)","tags":["Trainer Assignment"]}},"/v1/trainer-assignments/{assignmentId}":{"put":{"operationId":"TrainerAssignmentController.update","parameters":[{"in":"path","name":"assignmentId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{},"example":{"status":"inactive","notes":"Paused membership.","endDate":"2026-06-30T00:00:00.000Z"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Assignment updated successfully","data":{"id":"c4d5e6f7-0819-4a2b-8c3d-4e5f60718293","trainerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","userId":"2b3c4d5e-6f70-8a91-b2c3-d4e5f6071829","startDate":"2026-01-15T09:30:00.000Z","endDate":"2026-06-30T00:00:00.000Z","status":"inactive","notes":"Onboarded via referral."}}}},"description":"Success"}},"summary":"Update an assignment (status / notes / endDate)","tags":["Trainer Assignment"]},"delete":{"operationId":"TrainerAssignmentController.remove","parameters":[{"in":"path","name":"assignmentId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Assignment deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete a trainer/client assignment","tags":["Trainer Assignment"]}},"/v1/progress-entry-statuses/":{"get":{"operationId":"ProgressEntryStatusController.list","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/ProgressEntryStatusDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Progress entry statuses retrieved","data":[{"id":"b1a2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","createdAt":"2026-05-01T08:00:00.000Z","updatedAt":"2026-05-01T08:00:00.000Z","name":"To review","color":"#94a3b8","displayOrder":0,"active":true},{"id":"c2b3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","createdAt":"2026-05-01T08:00:00.000Z","updatedAt":"2026-05-01T08:00:00.000Z","name":"Reviewed","color":"#22c55e","displayOrder":1,"active":true}]}}},"description":"Success"}},"summary":"List the tenant progress-entry statuses (ordered by displayOrder)","tags":["Progress Entry Status"]},"put":{"operationId":"ProgressEntryStatusController.replaceList","requestBody":{"content":{"application/json":{"schema":{"items":{"type":"object","$ref":"#/components/schemas/ProgressEntryStatusUpsertDto"},"type":"array"},"example":[{"id":"b1a2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","name":"To review","color":"#94a3b8","displayOrder":0,"active":true},{"name":"Reviewed","color":"#22c55e","displayOrder":1,"active":true}]}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/ProgressEntryStatusDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Progress entry statuses updated","data":[{"id":"b1a2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","createdAt":"2026-05-01T08:00:00.000Z","updatedAt":"2026-05-01T08:00:00.000Z","name":"To review","color":"#94a3b8","displayOrder":0,"active":true},{"id":"c2b3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","createdAt":"2026-05-01T08:00:00.000Z","updatedAt":"2026-05-01T08:00:00.000Z","name":"Reviewed","color":"#22c55e","displayOrder":1,"active":true}]}}},"description":"Success"}},"summary":"Replace the whole status list (admin/owner only; diff-based upsert)","tags":["Progress Entry Status"]}},"/v1/app/trainers/{trainerId}/configuration":{"get":{"operationId":"AppConfigurationController.getAppConfiguration","parameters":[{"in":"path","name":"trainerId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/AppConfigurationDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"App configuration retrieved successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","reviewClientId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","general":{"appName":"Peak Performance","tagline":"Train smarter","contactEmail":"coach@example.com"},"styling":{"themeMode":"AUTOMATIC"},"features":{"workoutTrackingEnabled":true,"nutritionTrackingEnabled":true,"chatEnabled":true,"shopEnabled":false}}}}},"description":"ADMIN/TRAINER only. Returns the full app-configuration blob for the given trainer."}},"summary":"Get the app configuration for a trainer","tags":["App Configuration"],"description":"ADMIN/TRAINER only. Returns the full app-configuration blob for the given trainer."},"put":{"operationId":"AppConfigurationController.updateAppConfiguration","parameters":[{"in":"path","name":"trainerId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppConfigurationDto"},"example":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","reviewClientId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","general":{"appName":"Peak Performance","tagline":"Train smarter","contactEmail":"coach@example.com"},"styling":{"themeMode":"AUTOMATIC"},"features":{"workoutTrackingEnabled":true,"nutritionTrackingEnabled":true,"chatEnabled":true,"shopEnabled":false}}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/AppConfigurationDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"App configuration updated successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","reviewClientId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","general":{"appName":"Peak Performance","tagline":"Train smarter","contactEmail":"coach@example.com"},"styling":{"themeMode":"AUTOMATIC"},"features":{"workoutTrackingEnabled":true,"nutritionTrackingEnabled":true,"chatEnabled":true,"shopEnabled":false}}}}},"description":"ADMIN/TRAINER only. Persists the supplied configuration blob and returns the stored result."}},"summary":"Replace the app configuration for a trainer","tags":["App Configuration"],"description":"ADMIN/TRAINER only. Persists the supplied configuration blob and returns the stored result."}},"/v1/app/trainers/{trainerId}/review-client":{"post":{"operationId":"AppConfigurationController.createReviewClient","parameters":[{"in":"path","name":"trainerId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/UserDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Review client created successfully","data":{"id":"b7c8d9e0-1f2a-3b4c-5d6e-7f8a9b0c1d2e","email":"review-client@example.com","firstName":"Review","lastName":"Client","role":"CLIENT","isActive":true,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"}}}},"description":"ADMIN/TRAINER only. Provisions (or returns) the trainer app-review test client and returns it as a user."}},"summary":"Create a review client for a trainer","tags":["App Configuration"],"description":"ADMIN/TRAINER only. Provisions (or returns) the trainer app-review test client and returns it as a user."}},"/v1/app/configuration":{"get":{"operationId":"AppConfigurationController.getAppConfigurationForClient","parameters":[{"in":"query","name":"tenantId","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/AppConfigurationDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"App configuration retrieved successfully","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","reviewClientId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","general":{"appName":"Peak Performance","tagline":"Train smarter","contactEmail":"coach@example.com"},"styling":{"themeMode":"AUTOMATIC"},"features":{"workoutTrackingEnabled":true,"nutritionTrackingEnabled":true,"chatEnabled":true,"shopEnabled":false}}}}},"description":"Public (no role gate) — the current user is optional. Resolves the configuration for the caller, or for the `tenantId` query param when unauthenticated."}},"summary":"Get the app configuration for a client","tags":["App Configuration"],"description":"Public (no role gate) — the current user is optional. Resolves the configuration for the caller, or for the `tenantId` query param when unauthenticated."}},"/v1/media/":{"post":{"operationId":"MediaController.createMedia","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaDto"},"example":{"name":"Squat depth cue.mp4","type":"VIDEO","usageType":"LIBRARY","url":"https://cdn.protocolcrm.com/media/uploads/squat-depth-cue.mp4","fileType":"video/mp4","categoryIds":["c1a2b3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/MediaDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Media created successfully","data":{"id":"7b3e4c1a-9d2f-4a6b-8c0e-1f2a3b4c5d6e","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","name":"Squat depth cue.mp4","type":"VIDEO","usageType":"LIBRARY","url":"https://cdn.protocolcrm.com/media/9f8e7d6c/squat-depth-cue-720p.mp4","thumbnailUrl":"https://cdn.protocolcrm.com/media/9f8e7d6c/squat-depth-cue-thumb.jpg","providerName":"aws-media","providerId":"media/9f8e7d6c/squat-depth-cue.mp4","fileType":"video/mp4","userId":"d4c3b2a1-f6e5-4b8a-9c0d-6d5e4f3a2b1c","ownerId":"e5d4c3b2-a1f6-4c9b-8d0e-7f6a5b4c3d2e","visibility":"TEAM","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","categories":[],"inSync":true,"processingStatus":"ready"}}}},"description":"Success"}},"summary":"Create a media record","tags":["Media"]}},"/v1/media/tenant":{"get":{"operationId":"MediaController.getAllMediaForTenant","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Media retrieved successfully","data":{"items":[{"id":"7b3e4c1a-9d2f-4a6b-8c0e-1f2a3b4c5d6e","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","name":"Squat depth cue.mp4","type":"VIDEO","usageType":"LIBRARY","url":"https://cdn.protocolcrm.com/media/9f8e7d6c/squat-depth-cue-720p.mp4","thumbnailUrl":"https://cdn.protocolcrm.com/media/9f8e7d6c/squat-depth-cue-thumb.jpg","providerName":"aws-media","providerId":"media/9f8e7d6c/squat-depth-cue.mp4","fileType":"video/mp4","userId":"d4c3b2a1-f6e5-4b8a-9c0d-6d5e4f3a2b1c","ownerId":"e5d4c3b2-a1f6-4c9b-8d0e-7f6a5b4c3d2e","visibility":"TEAM","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","categories":[],"inSync":true,"processingStatus":"ready"}],"total":1,"page":0,"pageSize":20,"totalPages":1}}}},"description":"Paginated, 0-indexed (page/size). Forces usageType=LIBRARY and excludes pending/null-status rows. Optional filters: type, categoryIds, name, userId, ownerId, scope."}},"summary":"List LIBRARY media for the tenant (paginated)","tags":["Media"],"description":"Paginated, 0-indexed (page/size). Forces usageType=LIBRARY and excludes pending/null-status rows. Optional filters: type, categoryIds, name, userId, ownerId, scope."}},"/v1/media/user/{userId}":{"get":{"operationId":"MediaController.getMediaByUserId","parameters":[{"in":"path","name":"userId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Media retrieved successfully","data":{"items":[{"id":"7b3e4c1a-9d2f-4a6b-8c0e-1f2a3b4c5d6e","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","name":"Squat depth cue.mp4","type":"VIDEO","usageType":"LIBRARY","url":"https://cdn.protocolcrm.com/media/9f8e7d6c/squat-depth-cue-720p.mp4","thumbnailUrl":"https://cdn.protocolcrm.com/media/9f8e7d6c/squat-depth-cue-thumb.jpg","providerName":"aws-media","providerId":"media/9f8e7d6c/squat-depth-cue.mp4","fileType":"video/mp4","userId":"d4c3b2a1-f6e5-4b8a-9c0d-6d5e4f3a2b1c","ownerId":"e5d4c3b2-a1f6-4c9b-8d0e-7f6a5b4c3d2e","visibility":"TEAM","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","categories":[],"inSync":true,"processingStatus":"ready"}],"total":1,"page":0,"pageSize":20,"totalPages":1}}}},"description":"Public (no auth). Paginated 0-indexed. Returns the LIBRARY media for the given user id, excluding pending/null-status rows."}},"summary":"List a user's LIBRARY media (public, paginated)","tags":["Media"],"description":"Public (no auth). Paginated 0-indexed. Returns the LIBRARY media for the given user id, excluding pending/null-status rows."}},"/v1/media/{id}/status":{"get":{"operationId":"MediaController.getMediaStatus","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/MediaStatusResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Media status retrieved successfully","data":{"mediaId":"7b3e4c1a-9d2f-4a6b-8c0e-1f2a3b4c5d6e","processingStatus":"ready","outputs":{"mp4_720p":"https://cdn.protocolcrm.com/media/9f8e7d6c/squat-depth-cue-720p.mp4","thumb":"https://cdn.protocolcrm.com/media/9f8e7d6c/squat-depth-cue-thumb.jpg"},"urls":{"mp4_720p":"https://cdn.protocolcrm.com/media/9f8e7d6c/squat-depth-cue-720p.mp4","thumb":"https://cdn.protocolcrm.com/media/9f8e7d6c/squat-depth-cue-thumb.jpg"},"error":null}}}},"description":"Success"}},"summary":"Get media processing status","tags":["Media"]}},"/v1/media/{id}":{"get":{"operationId":"MediaController.getMediaById","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/MediaDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Media retrieved successfully","data":{"id":"7b3e4c1a-9d2f-4a6b-8c0e-1f2a3b4c5d6e","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","name":"Squat depth cue.mp4","type":"VIDEO","usageType":"LIBRARY","url":"https://cdn.protocolcrm.com/media/9f8e7d6c/squat-depth-cue-720p.mp4","thumbnailUrl":"https://cdn.protocolcrm.com/media/9f8e7d6c/squat-depth-cue-thumb.jpg","providerName":"aws-media","providerId":"media/9f8e7d6c/squat-depth-cue.mp4","fileType":"video/mp4","userId":"d4c3b2a1-f6e5-4b8a-9c0d-6d5e4f3a2b1c","ownerId":"e5d4c3b2-a1f6-4c9b-8d0e-7f6a5b4c3d2e","visibility":"TEAM","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","categories":[],"inSync":true,"processingStatus":"ready"}}}},"description":"Success"}},"summary":"Get a media record by id","tags":["Media"]},"put":{"operationId":"MediaController.updateMedia","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaDto"},"example":{"name":"Squat depth cue (revised).mp4","usageType":"LIBRARY","categoryIds":["c1a2b3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"]}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/MediaDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Media updated successfully","data":{"id":"7b3e4c1a-9d2f-4a6b-8c0e-1f2a3b4c5d6e","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","name":"Squat depth cue.mp4","type":"VIDEO","usageType":"LIBRARY","url":"https://cdn.protocolcrm.com/media/9f8e7d6c/squat-depth-cue-720p.mp4","thumbnailUrl":"https://cdn.protocolcrm.com/media/9f8e7d6c/squat-depth-cue-thumb.jpg","providerName":"aws-media","providerId":"media/9f8e7d6c/squat-depth-cue.mp4","fileType":"video/mp4","userId":"d4c3b2a1-f6e5-4b8a-9c0d-6d5e4f3a2b1c","ownerId":"e5d4c3b2-a1f6-4c9b-8d0e-7f6a5b4c3d2e","visibility":"TEAM","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","categories":[],"inSync":true,"processingStatus":"ready"}}}},"description":"Partial update — omitted fields are left unchanged."}},"summary":"Update a media record","tags":["Media"],"description":"Partial update — omitted fields are left unchanged."},"delete":{"operationId":"MediaController.deleteMedia","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Media deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete a media record","tags":["Media"]}},"/v1/media/upload":{"post":{"operationId":"MediaController.uploadMedia","requestBody":{"content":{"multipart/form-data":{"schema":{"properties":{"categoryId":{"type":"string"},"userId":{"type":"string"},"usageType":{"type":"string"},"preCompressed":{"type":"string"},"file":{"type":"string","format":"binary"}},"required":[],"type":"object"}}}},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/MediaDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Media uploaded successfully","data":{"id":"7b3e4c1a-9d2f-4a6b-8c0e-1f2a3b4c5d6e","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","name":"Squat depth cue.mp4","type":"VIDEO","usageType":"LIBRARY","url":"https://cdn.protocolcrm.com/media/9f8e7d6c/squat-depth-cue-720p.mp4","thumbnailUrl":"https://cdn.protocolcrm.com/media/9f8e7d6c/squat-depth-cue-thumb.jpg","providerName":"aws-media","providerId":"media/9f8e7d6c/squat-depth-cue.mp4","fileType":"video/mp4","userId":"d4c3b2a1-f6e5-4b8a-9c0d-6d5e4f3a2b1c","ownerId":"e5d4c3b2-a1f6-4c9b-8d0e-7f6a5b4c3d2e","visibility":"TEAM","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","categories":[],"inSync":true,"processingStatus":"ready"}}}},"description":"multipart/form-data: `file` plus optional form fields categoryId, userId, usageType, preCompressed. Runs the AWS media upload flow and creates the record."}},"summary":"Upload a media file (multipart)","tags":["Media"],"description":"multipart/form-data: `file` plus optional form fields categoryId, userId, usageType, preCompressed. Runs the AWS media upload flow and creates the record."}},"/v1/media/upload-url":{"post":{"operationId":"MediaController.getUploadUrl","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaUploadUrlRequestDto"},"example":{"filename":"squat-depth-cue.mp4","contentType":"video/mp4","categoryId":"c1a2b3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","usageType":"LIBRARY","userId":null,"preCompressed":false}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/MediaUploadUrlResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Upload URL generated successfully","data":{"uploadUrl":"https://protocol-media-uploads.s3.amazonaws.com/uploads/7b3e4c1a-9d2f-4a6b-8c0e-1f2a3b4c5d6e/squat-depth-cue.mp4?X-Amz-Signature=8f3c2a1b","mediaId":"7b3e4c1a-9d2f-4a6b-8c0e-1f2a3b4c5d6e","expiresAt":"2026-01-15T10:30:00.000Z"}}}},"description":"Success"}},"summary":"Generate an S3 presigned upload URL","tags":["Media"]}},"/v1/media/{id}/download":{"get":{"operationId":"MediaController.downloadMedia","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{}},"description":"Successful response"},"302":{"description":"For aws-media, redirects (302) to the processed CDN URL. Non-aws providers → 400. No JSON body.","content":{"application/json":{}}}},"summary":"Download a media file (302 redirect)","tags":["Media"],"description":"For aws-media, redirects (302) to the processed CDN URL. Non-aws providers → 400. No JSON body."}},"/v1/media/serve/{filename}":{"get":{"operationId":"MediaController.serveMedia","parameters":[{"in":"path","name":"filename","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{}},"description":"Successful response"},"302":{"description":"Public (no auth). Strips the extension to the media id and redirects (302) to the processed public URL for aws-media; other providers → 501. No JSON body.","content":{"application/json":{}}}},"summary":"Serve a media file by filename (public, 302 redirect)","tags":["Media"],"description":"Public (no auth). Strips the extension to the media id and redirects (302) to the processed public URL for aws-media; other providers → 501. No JSON body."}},"/v1/media/cloudinary/signature":{"post":{"operationId":"MediaController.getCloudinarySignature","responses":{"200":{"content":{"application/json":{}},"description":"Successful response"},"501":{"description":"Deferred — not yet migrated to the Node backend; currently returns 501 Not Implemented.","content":{"application/json":{}}}},"summary":"Generate a Cloudinary upload signature (deferred)","tags":["Media"],"description":"Deferred — not yet migrated to the Node backend; currently returns 501 Not Implemented."}},"/v1/media/drive/files":{"get":{"operationId":"MediaController.listGoogleDriveFiles","responses":{"200":{"content":{"application/json":{}},"description":"Successful response"},"501":{"description":"Public. Deferred — not yet migrated to the Node backend; currently returns 501 Not Implemented.","content":{"application/json":{}}}},"summary":"List Google Drive files (deferred)","tags":["Media"],"description":"Public. Deferred — not yet migrated to the Node backend; currently returns 501 Not Implemented."}},"/v1/media/drive/files/{fileId}":{"get":{"operationId":"MediaController.getGoogleDriveFile","parameters":[{"in":"path","name":"fileId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{}},"description":"Successful response"},"501":{"description":"Public. Deferred — not yet migrated to the Node backend; currently returns 501 Not Implemented.","content":{"application/json":{}}}},"summary":"Get a Google Drive file (deferred)","tags":["Media"],"description":"Public. Deferred — not yet migrated to the Node backend; currently returns 501 Not Implemented."}},"/v1/media/drive/import":{"post":{"operationId":"MediaController.importFromGoogleDrive","responses":{"200":{"content":{"application/json":{}},"description":"Successful response"},"501":{"description":"Public. Deferred — not yet migrated to the Node backend; currently returns 501 Not Implemented.","content":{"application/json":{}}}},"summary":"Import a file from Google Drive (deferred)","tags":["Media"],"description":"Public. Deferred — not yet migrated to the Node backend; currently returns 501 Not Implemented."}},"/v1/media-categories/":{"post":{"operationId":"MediaCategoryController.createCategory","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaCategoryDto"},"example":{"name":"Mobility drills","color":"#2E86AB","iconEmoji":"🧘","order":0,"categoryType":"MEDIA","description":"Warm-up and mobility video library"}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/MediaCategoryDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Media category created successfully","data":{"id":"c1a2b3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","createdAt":"2026-01-10T08:00:00.000Z","updatedAt":"2026-01-12T14:20:00.000Z","name":"Mobility drills","color":"#2E86AB","iconEmoji":"🧘","order":0,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","parentId":null,"userId":null,"resourceCount":12,"categoryType":"MEDIA","description":"Warm-up and mobility video library","avatarMedia":null,"externalId":null,"externalUpdatedAt":null}}}},"description":"Success"}},"summary":"Create a media category","tags":["Media Category"]},"get":{"operationId":"MediaCategoryController.getAllCategories","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Media categories retrieved successfully","data":{"items":[{"id":"c1a2b3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","createdAt":"2026-01-10T08:00:00.000Z","updatedAt":"2026-01-12T14:20:00.000Z","name":"Mobility drills","color":"#2E86AB","iconEmoji":"🧘","order":0,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","parentId":null,"userId":null,"resourceCount":12,"categoryType":"MEDIA","description":"Warm-up and mobility video library","avatarMedia":null,"externalId":null,"externalUpdatedAt":null}],"total":1,"page":0,"pageSize":20,"totalPages":1}}}},"description":"Public (no auth). Paginated 0-indexed. Optional filters: mediaName, userId, categoryType, externalId."}},"summary":"List media categories (public, paginated)","tags":["Media Category"],"description":"Public (no auth). Paginated 0-indexed. Optional filters: mediaName, userId, categoryType, externalId."}},"/v1/media-categories/{id}":{"get":{"operationId":"MediaCategoryController.getCategoryById","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/MediaCategoryDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Media category retrieved successfully","data":{"id":"c1a2b3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","createdAt":"2026-01-10T08:00:00.000Z","updatedAt":"2026-01-12T14:20:00.000Z","name":"Mobility drills","color":"#2E86AB","iconEmoji":"🧘","order":0,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","parentId":null,"userId":null,"resourceCount":12,"categoryType":"MEDIA","description":"Warm-up and mobility video library","avatarMedia":null,"externalId":null,"externalUpdatedAt":null}}}},"description":"Success"}},"summary":"Get a media category by id","tags":["Media Category"]},"put":{"operationId":"MediaCategoryController.updateCategory","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaCategoryDto"},"example":{"name":"Mobility drills","color":"#2E86AB","iconEmoji":"🧘","order":0,"categoryType":"MEDIA","description":"Warm-up and mobility video library"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/MediaCategoryDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Media category updated successfully","data":{"id":"c1a2b3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d","createdAt":"2026-01-10T08:00:00.000Z","updatedAt":"2026-01-12T14:20:00.000Z","name":"Mobility drills","color":"#2E86AB","iconEmoji":"🧘","order":0,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","parentId":null,"userId":null,"resourceCount":12,"categoryType":"MEDIA","description":"Warm-up and mobility video library","avatarMedia":null,"externalId":null,"externalUpdatedAt":null}}}},"description":"Success"}},"summary":"Update a media category","tags":["Media Category"]},"delete":{"operationId":"MediaCategoryController.deleteCategory","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Media category deleted successfully","data":null}}},"description":"Optional query param deleteMedia=true also deletes the media in the category (default false)."}},"summary":"Delete a media category","tags":["Media Category"],"description":"Optional query param deleteMedia=true also deletes the media in the category (default false)."}},"/v1/media-shares/":{"post":{"operationId":"MediaShareController.createShare","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMediaShareRequestDto"},"example":{"mediaId":"7b3e4c1a-9d2f-4a6b-8c0e-1f2a3b4c5d6e","shareType":"USER_SPECIFIC","permission":"READ","sharedWithUserIds":["d4c3b2a1-f6e5-4b8a-9c0d-6d5e4f3a2b1c"],"expiresAt":"2026-02-15T00:00:00.000Z","sendNotification":true}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/CreateMediaShareResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Share created successfully","data":{"shares":[{"id":"a9b8c7d6-e5f4-4a3b-8c2d-1e0f9a8b7c6d","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","mediaId":"7b3e4c1a-9d2f-4a6b-8c0e-1f2a3b4c5d6e","ownerId":"e5d4c3b2-a1f6-4c9b-8d0e-7f6a5b4c3d2e","ownerName":"Dijana Keri","shareType":"USER_SPECIFIC","permission":"READ","sharedWithUserId":"d4c3b2a1-f6e5-4b8a-9c0d-6d5e4f3a2b1c","sharedWithUserName":"Marko Petrovic","sharedWithUserEmail":"marko@example.com","isActive":true,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"}],"notificationsSent":1}}}},"description":"Success"}},"summary":"Create a media share (public link or user-specific)","tags":["Media Share"]}},"/v1/media-shares/media/{mediaId}":{"get":{"operationId":"MediaShareController.getSharesForMedia","parameters":[{"in":"path","name":"mediaId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/MediaShareDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Shares retrieved successfully","data":[{"id":"a9b8c7d6-e5f4-4a3b-8c2d-1e0f9a8b7c6d","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","mediaId":"7b3e4c1a-9d2f-4a6b-8c0e-1f2a3b4c5d6e","ownerId":"e5d4c3b2-a1f6-4c9b-8d0e-7f6a5b4c3d2e","ownerName":"Dijana Keri","shareType":"USER_SPECIFIC","permission":"READ","sharedWithUserId":"d4c3b2a1-f6e5-4b8a-9c0d-6d5e4f3a2b1c","sharedWithUserName":"Marko Petrovic","sharedWithUserEmail":"marko@example.com","isActive":true,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"}]}}},"description":"Success"}},"summary":"List active shares for a media item","tags":["Media Share"]}},"/v1/media-shares/shared-with-me":{"get":{"operationId":"MediaShareController.getSharedWithMe","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Shared media retrieved successfully","data":{"items":[{"share":{"id":"b8c7d6e5-f4a3-4b2c-8d1e-0f9a8b7c6d5e","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","mediaId":"7b3e4c1a-9d2f-4a6b-8c0e-1f2a3b4c5d6e","ownerId":"e5d4c3b2-a1f6-4c9b-8d0e-7f6a5b4c3d2e","ownerName":"Dijana Keri","shareType":"PUBLIC","permission":"READ","publicToken":"3f2a1b0c-9d8e-4f7a-8b6c-5d4e3f2a1b0c","publicUrl":"https://api.protocolcrm.com/v1/media-shares/public/3f2a1b0c-9d8e-4f7a-8b6c-5d4e3f2a1b0c","isActive":true,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"},"media":{"id":"7b3e4c1a-9d2f-4a6b-8c0e-1f2a3b4c5d6e","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","name":"Squat depth cue.mp4","type":"VIDEO","usageType":"LIBRARY","url":"https://cdn.protocolcrm.com/media/9f8e7d6c/squat-depth-cue-720p.mp4","thumbnailUrl":"https://cdn.protocolcrm.com/media/9f8e7d6c/squat-depth-cue-thumb.jpg","providerName":"aws-media","fileType":"video/mp4","ownerId":"e5d4c3b2-a1f6-4c9b-8d0e-7f6a5b4c3d2e","visibility":"TEAM","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","categories":[],"inSync":true,"processingStatus":"ready"}}],"total":1,"page":0,"pageSize":20,"totalPages":1}}}},"description":"Paginated 0-indexed. Returns active, unexpired incoming shares with the media attached. Optional filters: permission, userId (trainers/owners only)."}},"summary":"List media shared with the current user (paginated)","tags":["Media Share"],"description":"Paginated 0-indexed. Returns active, unexpired incoming shares with the media attached. Optional filters: permission, userId (trainers/owners only)."}},"/v1/media-shares/{shareId}":{"put":{"operationId":"MediaShareController.updateShare","parameters":[{"in":"path","name":"shareId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMediaShareRequestDto"},"example":{"permission":"WRITE","expiresAt":"2026-03-01T00:00:00.000Z","isActive":true}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/MediaShareDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Share updated successfully","data":{"id":"a9b8c7d6-e5f4-4a3b-8c2d-1e0f9a8b7c6d","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","mediaId":"7b3e4c1a-9d2f-4a6b-8c0e-1f2a3b4c5d6e","ownerId":"e5d4c3b2-a1f6-4c9b-8d0e-7f6a5b4c3d2e","ownerName":"Dijana Keri","shareType":"USER_SPECIFIC","permission":"WRITE","sharedWithUserId":"d4c3b2a1-f6e5-4b8a-9c0d-6d5e4f3a2b1c","sharedWithUserName":"Marko Petrovic","sharedWithUserEmail":"marko@example.com","isActive":true,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"}}}},"description":"Owner-only. Absent fields are left unchanged."}},"summary":"Update a media share","tags":["Media Share"],"description":"Owner-only. Absent fields are left unchanged."},"delete":{"operationId":"MediaShareController.revokeShare","parameters":[{"in":"path","name":"shareId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Share revoked successfully","data":null}}},"description":"Owner-only. Soft-deactivates the share (isActive → false)."}},"summary":"Revoke a media share","tags":["Media Share"],"description":"Owner-only. Soft-deactivates the share (isActive → false)."}},"/v1/media-shares/public/{token}":{"get":{"operationId":"MediaShareController.getPublicMedia","parameters":[{"in":"path","name":"token","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/SharedMediaDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Media retrieved successfully","data":{"share":{"id":"b8c7d6e5-f4a3-4b2c-8d1e-0f9a8b7c6d5e","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","mediaId":"7b3e4c1a-9d2f-4a6b-8c0e-1f2a3b4c5d6e","ownerId":"e5d4c3b2-a1f6-4c9b-8d0e-7f6a5b4c3d2e","ownerName":"Dijana Keri","shareType":"PUBLIC","permission":"READ","publicToken":"3f2a1b0c-9d8e-4f7a-8b6c-5d4e3f2a1b0c","publicUrl":"https://api.protocolcrm.com/v1/media-shares/public/3f2a1b0c-9d8e-4f7a-8b6c-5d4e3f2a1b0c","isActive":true,"tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"},"media":{"id":"7b3e4c1a-9d2f-4a6b-8c0e-1f2a3b4c5d6e","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","name":"Squat depth cue.mp4","type":"VIDEO","usageType":"LIBRARY","url":"https://cdn.protocolcrm.com/media/9f8e7d6c/squat-depth-cue-720p.mp4","thumbnailUrl":"https://cdn.protocolcrm.com/media/9f8e7d6c/squat-depth-cue-thumb.jpg","providerName":"aws-media","fileType":"video/mp4","ownerId":"e5d4c3b2-a1f6-4c9b-8d0e-7f6a5b4c3d2e","visibility":"TEAM","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","categories":[],"inSync":true,"processingStatus":"ready"}}}}},"description":"Public (no auth). Resolves an active, unexpired public share token to its media."}},"summary":"Get media via a public share token (public)","tags":["Media Share"],"description":"Public (no auth). Resolves an active, unexpired public share token to its media."}},"/v1/public/media/upload":{"post":{"operationId":"PublicMediaController.uploadMedia","requestBody":{"content":{"multipart/form-data":{"schema":{"properties":{"usageType":{"type":"string"},"formId":{"type":"string"},"file":{"type":"string","format":"binary"}},"required":[],"type":"object"}}}},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/MediaDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Media uploaded successfully","data":{"id":"7b3e4c1a-9d2f-4a6b-8c0e-1f2a3b4c5d6e","createdAt":"2026-01-15T09:30:00.000Z","updatedAt":"2026-01-15T09:30:00.000Z","name":"intake-form-photo.jpg","type":"IMAGE","usageType":"CONVERSATION","url":"https://cdn.protocolcrm.com/media/2a3b4c5d/intake-form-photo.jpg","thumbnailUrl":"https://cdn.protocolcrm.com/media/2a3b4c5d/intake-form-photo-thumb.jpg","providerName":"aws-media","fileType":"image/jpeg","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","categories":[],"inSync":true,"processingStatus":"ready"}}}},"description":"Public (no auth). multipart/form-data: `file` plus optional form fields usageType and formId (used to resolve the tenant). Runs the AWS media upload flow."}},"summary":"Upload a media file (public, multipart)","tags":["Public Media"],"description":"Public (no auth). multipart/form-data: `file` plus optional form fields usageType and formId (used to resolve the tenant). Runs the AWS media upload flow."}},"/v1/nutrition/food-items/":{"post":{"operationId":"ProtocolFoodItemController.createFoodItem","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProtocolFoodItemDto"},"example":{"id":"7c9e6679-7425-40de-944b-e07fc1f90ae7","createdAt":"2026-02-10T08:15:00.000Z","updatedAt":"2026-02-10T08:15:00.000Z","createdBy":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","name":"Chicken breast, cooked","category":"POULTRY","subcategory":"POULTRY_CHICKEN","aliases":["grilled chicken breast"],"measurements":[{"name":"breast","grams":120,"amount":1}],"caloriesPer100g":165,"proteinPer100g":31,"fatPer100g":4,"carbsPer100g":0,"caloriesPerGram":1.65,"proteinPerGram":0.31,"fatPerGram":0.04,"carbsPerGram":0,"validated":true,"isLiquid":false,"dataSource":"USDA","aiEstimated":false,"isPublicLibrary":true}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProtocolFoodItemDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Food item created successfully","data":{"id":"7c9e6679-7425-40de-944b-e07fc1f90ae7","createdAt":"2026-02-10T08:15:00.000Z","updatedAt":"2026-02-10T08:15:00.000Z","createdBy":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","name":"Chicken breast, cooked","category":"POULTRY","subcategory":"POULTRY_CHICKEN","aliases":["grilled chicken breast"],"measurements":[{"name":"breast","grams":120,"amount":1}],"caloriesPer100g":165,"proteinPer100g":31,"fatPer100g":4,"carbsPer100g":0,"caloriesPerGram":1.65,"proteinPerGram":0.31,"fatPerGram":0.04,"carbsPerGram":0,"validated":true,"isLiquid":false,"dataSource":"USDA","aiEstimated":false,"isPublicLibrary":true}}}},"description":"Success"}},"summary":"Create a food item","tags":["Protocol Food Item"]},"get":{"operationId":"ProtocolFoodItemController.listFoodItems","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Food items retrieved successfully","data":{"items":[{"id":"7c9e6679-7425-40de-944b-e07fc1f90ae7","createdAt":"2026-02-10T08:15:00.000Z","updatedAt":"2026-02-10T08:15:00.000Z","createdBy":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","name":"Chicken breast, cooked","category":"POULTRY","subcategory":"POULTRY_CHICKEN","aliases":["grilled chicken breast"],"measurements":[{"name":"breast","grams":120,"amount":1}],"caloriesPer100g":165,"proteinPer100g":31,"fatPer100g":4,"carbsPer100g":0,"caloriesPerGram":1.65,"proteinPerGram":0.31,"fatPerGram":0.04,"carbsPerGram":0,"validated":true,"isLiquid":false,"dataSource":"USDA","aiEstimated":false,"isPublicLibrary":true}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List food items with filters and pagination","tags":["Protocol Food Item"]}},"/v1/nutrition/food-items/filters":{"get":{"operationId":"ProtocolFoodItemController.getFilterOptions","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProtocolFoodItemFiltersResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Filter options retrieved successfully","data":{"categories":["POULTRY","VEGETABLES","DAIRY"],"subcategories":["POULTRY_CHICKEN","VEGETABLES_LEAFY"]}}}},"description":"Success"}},"summary":"List distinct food-item categories and subcategories","tags":["Protocol Food Item"]}},"/v1/nutrition/food-items/discover":{"post":{"operationId":"ProtocolFoodItemController.discoverFood","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Food discovery completed","data":[{"name":"Rolled oats","category":"GRAINS","subcategory":null,"aliases":["oatmeal"],"measurements":[{"name":"cup","grams":80,"amount":1}],"caloriesPerGram":3.8,"proteinPerGram":0.13,"fatPerGram":0.07,"carbsPerGram":0.67,"fiberPerGram":0.1,"sugarPerGram":0.01,"sodiumPerGram":0,"caloriesPer100g":380,"proteinPer100g":13,"fatPer100g":7,"carbsPer100g":67,"fiberPer100g":10,"sugarPer100g":1,"sodiumPer100g":0,"fdcId":"169705","barcode":null,"validated":false,"isLiquid":false,"dataSource":"USDA","aiEstimated":false,"searchStrategyUsed":"usda_search"}]}}},"description":"Success"}},"summary":"Discover foods from external databases (USDA + Open Food Facts + AI fallback)","tags":["Protocol Food Item"]}},"/v1/nutrition/food-items/{id}":{"get":{"operationId":"ProtocolFoodItemController.getFoodItemById","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProtocolFoodItemDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Food item retrieved successfully","data":{"id":"7c9e6679-7425-40de-944b-e07fc1f90ae7","createdAt":"2026-02-10T08:15:00.000Z","updatedAt":"2026-02-10T08:15:00.000Z","createdBy":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","name":"Chicken breast, cooked","category":"POULTRY","subcategory":"POULTRY_CHICKEN","aliases":["grilled chicken breast"],"measurements":[{"name":"breast","grams":120,"amount":1}],"caloriesPer100g":165,"proteinPer100g":31,"fatPer100g":4,"carbsPer100g":0,"caloriesPerGram":1.65,"proteinPerGram":0.31,"fatPerGram":0.04,"carbsPerGram":0,"validated":true,"isLiquid":false,"dataSource":"USDA","aiEstimated":false,"isPublicLibrary":true}}}},"description":"Success"}},"summary":"Get a food item by id","tags":["Protocol Food Item"]},"put":{"operationId":"ProtocolFoodItemController.updateFoodItem","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProtocolFoodItemDto"},"example":{"id":"7c9e6679-7425-40de-944b-e07fc1f90ae7","createdAt":"2026-02-10T08:15:00.000Z","updatedAt":"2026-02-10T08:15:00.000Z","createdBy":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","name":"Chicken breast, cooked","category":"POULTRY","subcategory":"POULTRY_CHICKEN","aliases":["grilled chicken breast"],"measurements":[{"name":"breast","grams":120,"amount":1}],"caloriesPer100g":165,"proteinPer100g":31,"fatPer100g":4,"carbsPer100g":0,"caloriesPerGram":1.65,"proteinPerGram":0.31,"fatPerGram":0.04,"carbsPerGram":0,"validated":true,"isLiquid":false,"dataSource":"USDA","aiEstimated":false,"isPublicLibrary":true}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProtocolFoodItemDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Food item updated successfully","data":{"id":"7c9e6679-7425-40de-944b-e07fc1f90ae7","createdAt":"2026-02-10T08:15:00.000Z","updatedAt":"2026-02-10T08:15:00.000Z","createdBy":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","name":"Chicken breast, cooked","category":"POULTRY","subcategory":"POULTRY_CHICKEN","aliases":["grilled chicken breast"],"measurements":[{"name":"breast","grams":120,"amount":1}],"caloriesPer100g":165,"proteinPer100g":31,"fatPer100g":4,"carbsPer100g":0,"caloriesPerGram":1.65,"proteinPerGram":0.31,"fatPerGram":0.04,"carbsPerGram":0,"validated":true,"isLiquid":false,"dataSource":"USDA","aiEstimated":false,"isPublicLibrary":true}}}},"description":"Success"}},"summary":"Update a food item","tags":["Protocol Food Item"]},"delete":{"operationId":"ProtocolFoodItemController.deleteFoodItem","parameters":[{"in":"path","name":"id","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Food item deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete a food item","tags":["Protocol Food Item"]}},"/v1/nutrition/templates/":{"post":{"operationId":"NutritionTemplateController.createNutritionTemplate","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NutritionTemplateDto"},"example":{"id":"2d1e4f60-8a3b-4c5d-9e0f-1a2b3c4d5e6f","createdAt":"2026-03-01T07:00:00.000Z","updatedAt":"2026-03-01T07:00:00.000Z","name":"High-protein breakfast","description":"Around 40g protein to start the day","items":[{"type":"MEAL","name":"Greek yogurt","quantity":200,"measureName":"g","nutrients":{"calories":120,"protein":20,"carbs":8,"fat":1,"fiber":0}}],"nutrients":{"calories":400,"protein":40,"carbs":30,"fat":12,"fiber":5},"image":null,"noImage":null,"isTemplate":true,"attachments":null,"translations":null,"userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","ownerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","visibility":"PRIVATE","tags":["breakfast","high-protein"],"categoryId":null,"externalId":null,"externalUpdatedAt":null,"templateMode":"SIMPLE","metadata":null}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/NutritionTemplateDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Nutrition template created successfully","data":{"id":"2d1e4f60-8a3b-4c5d-9e0f-1a2b3c4d5e6f","createdAt":"2026-03-01T07:00:00.000Z","updatedAt":"2026-03-01T07:00:00.000Z","name":"High-protein breakfast","description":"Around 40g protein to start the day","items":[{"type":"MEAL","name":"Greek yogurt","quantity":200,"measureName":"g","nutrients":{"calories":120,"protein":20,"carbs":8,"fat":1,"fiber":0}}],"nutrients":{"calories":400,"protein":40,"carbs":30,"fat":12,"fiber":5},"image":null,"noImage":null,"isTemplate":true,"attachments":null,"translations":null,"userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","ownerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","visibility":"PRIVATE","tags":["breakfast","high-protein"],"categoryId":null,"externalId":null,"externalUpdatedAt":null,"templateMode":"SIMPLE","metadata":null}}}},"description":"Success"}},"summary":"Create a nutrition template","tags":["Nutrition Template"]},"get":{"operationId":"NutritionTemplateController.listNutritionTemplates","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Nutrition templates retrieved successfully","data":{"items":[{"id":"2d1e4f60-8a3b-4c5d-9e0f-1a2b3c4d5e6f","createdAt":"2026-03-01T07:00:00.000Z","updatedAt":"2026-03-01T07:00:00.000Z","name":"High-protein breakfast","description":"Around 40g protein to start the day","items":[{"type":"MEAL","name":"Greek yogurt","quantity":200,"measureName":"g","nutrients":{"calories":120,"protein":20,"carbs":8,"fat":1,"fiber":0}}],"nutrients":{"calories":400,"protein":40,"carbs":30,"fat":12,"fiber":5},"image":null,"noImage":null,"isTemplate":true,"attachments":null,"translations":null,"userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","ownerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","visibility":"PRIVATE","tags":["breakfast","high-protein"],"categoryId":null,"externalId":null,"externalUpdatedAt":null,"templateMode":"SIMPLE","metadata":null}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List nutrition templates with filters and pagination","tags":["Nutrition Template"]}},"/v1/nutrition/templates/{templateId}/import":{"post":{"operationId":"NutritionTemplateController.importNutritionTemplate","parameters":[{"in":"path","name":"templateId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/NutritionTemplateDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Nutrition template imported to your library","data":{"id":"2d1e4f60-8a3b-4c5d-9e0f-1a2b3c4d5e6f","createdAt":"2026-03-01T07:00:00.000Z","updatedAt":"2026-03-01T07:00:00.000Z","name":"High-protein breakfast","description":"Around 40g protein to start the day","items":[{"type":"MEAL","name":"Greek yogurt","quantity":200,"measureName":"g","nutrients":{"calories":120,"protein":20,"carbs":8,"fat":1,"fiber":0}}],"nutrients":{"calories":400,"protein":40,"carbs":30,"fat":12,"fiber":5},"image":null,"noImage":null,"isTemplate":true,"attachments":null,"translations":null,"userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","ownerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","visibility":"PRIVATE","tags":["breakfast","high-protein"],"categoryId":null,"externalId":null,"externalUpdatedAt":null,"templateMode":"SIMPLE","metadata":null}}}},"description":"Success"}},"summary":"Import a team/own template into the caller's private library","tags":["Nutrition Template"]}},"/v1/nutrition/templates/{templateId}":{"get":{"operationId":"NutritionTemplateController.getNutritionTemplateById","parameters":[{"in":"path","name":"templateId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/NutritionTemplateDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Nutrition template retrieved successfully","data":{"id":"2d1e4f60-8a3b-4c5d-9e0f-1a2b3c4d5e6f","createdAt":"2026-03-01T07:00:00.000Z","updatedAt":"2026-03-01T07:00:00.000Z","name":"High-protein breakfast","description":"Around 40g protein to start the day","items":[{"type":"MEAL","name":"Greek yogurt","quantity":200,"measureName":"g","nutrients":{"calories":120,"protein":20,"carbs":8,"fat":1,"fiber":0}}],"nutrients":{"calories":400,"protein":40,"carbs":30,"fat":12,"fiber":5},"image":null,"noImage":null,"isTemplate":true,"attachments":null,"translations":null,"userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","ownerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","visibility":"PRIVATE","tags":["breakfast","high-protein"],"categoryId":null,"externalId":null,"externalUpdatedAt":null,"templateMode":"SIMPLE","metadata":null}}}},"description":"Success"}},"summary":"Get a nutrition template by id","tags":["Nutrition Template"]},"put":{"operationId":"NutritionTemplateController.updateNutritionTemplate","parameters":[{"in":"path","name":"templateId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NutritionTemplateDto"},"example":{"id":"2d1e4f60-8a3b-4c5d-9e0f-1a2b3c4d5e6f","createdAt":"2026-03-01T07:00:00.000Z","updatedAt":"2026-03-01T07:00:00.000Z","name":"High-protein breakfast","description":"Around 40g protein to start the day","items":[{"type":"MEAL","name":"Greek yogurt","quantity":200,"measureName":"g","nutrients":{"calories":120,"protein":20,"carbs":8,"fat":1,"fiber":0}}],"nutrients":{"calories":400,"protein":40,"carbs":30,"fat":12,"fiber":5},"image":null,"noImage":null,"isTemplate":true,"attachments":null,"translations":null,"userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","ownerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","visibility":"PRIVATE","tags":["breakfast","high-protein"],"categoryId":null,"externalId":null,"externalUpdatedAt":null,"templateMode":"SIMPLE","metadata":null}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/NutritionTemplateDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Nutrition template updated successfully","data":{"id":"2d1e4f60-8a3b-4c5d-9e0f-1a2b3c4d5e6f","createdAt":"2026-03-01T07:00:00.000Z","updatedAt":"2026-03-01T07:00:00.000Z","name":"High-protein breakfast","description":"Around 40g protein to start the day","items":[{"type":"MEAL","name":"Greek yogurt","quantity":200,"measureName":"g","nutrients":{"calories":120,"protein":20,"carbs":8,"fat":1,"fiber":0}}],"nutrients":{"calories":400,"protein":40,"carbs":30,"fat":12,"fiber":5},"image":null,"noImage":null,"isTemplate":true,"attachments":null,"translations":null,"userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","ownerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","visibility":"PRIVATE","tags":["breakfast","high-protein"],"categoryId":null,"externalId":null,"externalUpdatedAt":null,"templateMode":"SIMPLE","metadata":null}}}},"description":"Success"}},"summary":"Update a nutrition template","tags":["Nutrition Template"]},"delete":{"operationId":"NutritionTemplateController.deleteNutritionTemplate","parameters":[{"in":"path","name":"templateId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Nutrition template deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete a nutrition template","tags":["Nutrition Template"]}},"/v1/nutrition/templates/{templateId}/task":{"post":{"operationId":"NutritionTemplateController.generateNutritionTemplate","parameters":[{"in":"path","name":"templateId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{}},"description":"Successful response"},"501":{"description":"Success","content":{"application/json":{"example":{"success":false,"message":"Nutrition template AI generation is not yet migrated","data":null}}}}},"summary":"Schedule AI nutrition-template generation (not yet migrated)","tags":["Nutrition Template"]},"get":{"operationId":"NutritionTemplateController.getLatestNutritionTemplateGeneration","parameters":[{"in":"path","name":"templateId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{}},"description":"Successful response"},"501":{"description":"Success","content":{"application/json":{"example":{"success":false,"message":"Nutrition template AI generation is not yet migrated","data":null}}}}},"summary":"Get latest AI nutrition-template generation status (not yet migrated)","tags":["Nutrition Template"]},"delete":{"operationId":"NutritionTemplateController.deleteNutritionTemplateGeneration","parameters":[{"in":"path","name":"templateId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{}},"description":"Successful response"},"501":{"description":"Success","content":{"application/json":{"example":{"success":false,"message":"Nutrition template AI generation is not yet migrated","data":null}}}}},"summary":"Delete AI nutrition-template generation task (not yet migrated)","tags":["Nutrition Template"]}},"/v1/nutrition/tracking/analyze":{"post":{"operationId":"NutritionTrackingController.analyzeMeal","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyzeMealDto"},"example":{"text":"2 eggs and a slice of toast","media":null}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/PreparedNutritionLogDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Meal analyzed successfully","data":{"inputType":"TEXT","originalInput":"2 eggs and a slice of toast","image":null,"items":[{"name":"Eggs","quantity":2,"measureLabel":"large","nutrients":{"calories":140,"protein":12,"carbs":1,"fat":10,"fiber":0}},{"name":"Whole wheat toast","quantity":1,"measureLabel":"slice","nutrients":{"calories":80,"protein":4,"carbs":14,"fat":1,"fiber":2}}],"totalNutrients":{"calories":220,"protein":16,"carbs":15,"fat":11,"fiber":2}}}}},"description":"Success"}},"summary":"Analyze a meal (text or image) into a prepared nutrition log","tags":["Nutrition Tracking"]}},"/v1/nutrition/tracking/describe-image":{"post":{"operationId":"NutritionTrackingController.describeMealImage","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DescribeMealImageDto"},"example":{"media":{"url":"https://cdn.protocolcrm.com/meals/breakfast.jpg"},"language":"en"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/MealImageDescriptionDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Meal image described successfully","data":{"text":"A bowl of oatmeal topped with banana slices and a spoonful of peanut butter."}}}},"description":"Success"}},"summary":"Turn a meal photo into an editable text description","tags":["Nutrition Tracking"]}},"/v1/nutrition/tracking/":{"post":{"operationId":"NutritionTrackingController.createLog","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateNutritionLogDto"},"example":{"userId":"c1d2e3f4-a5b6-7c8d-9e0f-1a2b3c4d5e6f","date":"2026-03-15","time":"08:30","inputType":"TEXT","originalInput":"2 eggs and a slice of toast","items":[{"name":"Eggs","quantity":2,"measureLabel":"large","nutrients":{"calories":140,"protein":12,"carbs":1,"fat":10,"fiber":0}},{"name":"Whole wheat toast","quantity":1,"measureLabel":"slice","nutrients":{"calories":80,"protein":4,"carbs":14,"fat":1,"fiber":2}}],"notes":null}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/NutritionLogDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Nutrition log created successfully","data":{"id":"b3d2f1a0-5c6d-4e7f-8a9b-0c1d2e3f4a5b","userId":"c1d2e3f4-a5b6-7c8d-9e0f-1a2b3c4d5e6f","loggedBy":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","logDate":"2026-03-15","logTime":"08:30","inputType":"TEXT","originalInput":"2 eggs and a slice of toast","image":null,"items":[{"name":"Eggs","quantity":2,"measureLabel":"large","nutrients":{"calories":140,"protein":12,"carbs":1,"fat":10,"fiber":0}},{"name":"Whole wheat toast","quantity":1,"measureLabel":"slice","nutrients":{"calories":80,"protein":4,"carbs":14,"fat":1,"fiber":2}}],"totalNutrients":{"calories":220,"protein":16,"carbs":15,"fat":11,"fiber":2},"notes":null,"nutritionTemplateId":null,"createdAt":"2026-03-15T08:31:00.000Z","updatedAt":"2026-03-15T08:31:00.000Z"}}}},"description":"Success"}},"summary":"Create a nutrition log","tags":["Nutrition Tracking"]},"get":{"operationId":"NutritionTrackingController.listLogs","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Nutrition logs retrieved successfully","data":{"items":[{"id":"b3d2f1a0-5c6d-4e7f-8a9b-0c1d2e3f4a5b","userId":"c1d2e3f4-a5b6-7c8d-9e0f-1a2b3c4d5e6f","loggedBy":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","logDate":"2026-03-15","logTime":"08:30","inputType":"TEXT","originalInput":"2 eggs and a slice of toast","image":null,"items":[{"name":"Eggs","quantity":2,"measureLabel":"large","nutrients":{"calories":140,"protein":12,"carbs":1,"fat":10,"fiber":0}},{"name":"Whole wheat toast","quantity":1,"measureLabel":"slice","nutrients":{"calories":80,"protein":4,"carbs":14,"fat":1,"fiber":2}}],"totalNutrients":{"calories":220,"protein":16,"carbs":15,"fat":11,"fiber":2},"notes":null,"nutritionTemplateId":null,"createdAt":"2026-03-15T08:31:00.000Z","updatedAt":"2026-03-15T08:31:00.000Z"}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List nutrition logs with pagination","tags":["Nutrition Tracking"]}},"/v1/nutrition/tracking/daily":{"get":{"operationId":"NutritionTrackingController.getDailySummary","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/DailyNutritionSummaryDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Daily nutrition summary retrieved successfully","data":{"date":"2026-03-15","userId":"c1d2e3f4-a5b6-7c8d-9e0f-1a2b3c4d5e6f","totalNutrients":{"calories":1800,"protein":140,"carbs":160,"fat":60,"fiber":25},"logs":[{"id":"b3d2f1a0-5c6d-4e7f-8a9b-0c1d2e3f4a5b","userId":"c1d2e3f4-a5b6-7c8d-9e0f-1a2b3c4d5e6f","loggedBy":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","logDate":"2026-03-15","logTime":"08:30","inputType":"TEXT","originalInput":"2 eggs and a slice of toast","image":null,"items":[{"name":"Eggs","quantity":2,"measureLabel":"large","nutrients":{"calories":140,"protein":12,"carbs":1,"fat":10,"fiber":0}},{"name":"Whole wheat toast","quantity":1,"measureLabel":"slice","nutrients":{"calories":80,"protein":4,"carbs":14,"fat":1,"fiber":2}}],"totalNutrients":{"calories":220,"protein":16,"carbs":15,"fat":11,"fiber":2},"notes":null,"nutritionTemplateId":null,"createdAt":"2026-03-15T08:31:00.000Z","updatedAt":"2026-03-15T08:31:00.000Z"}],"logCount":1,"templateComparison":null}}}},"description":"Success"}},"summary":"Get a daily nutrition summary (defaults to today)","tags":["Nutrition Tracking"]}},"/v1/nutrition/tracking/{logId}":{"get":{"operationId":"NutritionTrackingController.getLogById","parameters":[{"in":"path","name":"logId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/NutritionLogDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Nutrition log retrieved successfully","data":{"id":"b3d2f1a0-5c6d-4e7f-8a9b-0c1d2e3f4a5b","userId":"c1d2e3f4-a5b6-7c8d-9e0f-1a2b3c4d5e6f","loggedBy":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","logDate":"2026-03-15","logTime":"08:30","inputType":"TEXT","originalInput":"2 eggs and a slice of toast","image":null,"items":[{"name":"Eggs","quantity":2,"measureLabel":"large","nutrients":{"calories":140,"protein":12,"carbs":1,"fat":10,"fiber":0}},{"name":"Whole wheat toast","quantity":1,"measureLabel":"slice","nutrients":{"calories":80,"protein":4,"carbs":14,"fat":1,"fiber":2}}],"totalNutrients":{"calories":220,"protein":16,"carbs":15,"fat":11,"fiber":2},"notes":null,"nutritionTemplateId":null,"createdAt":"2026-03-15T08:31:00.000Z","updatedAt":"2026-03-15T08:31:00.000Z"}}}},"description":"Success"}},"summary":"Get a nutrition log by id","tags":["Nutrition Tracking"]},"put":{"operationId":"NutritionTrackingController.updateLog","parameters":[{"in":"path","name":"logId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateNutritionLogDto"},"example":{"date":"2026-03-15","time":"12:30","items":[{"name":"Eggs","quantity":2,"measureLabel":"large","nutrients":{"calories":140,"protein":12,"carbs":1,"fat":10,"fiber":0}},{"name":"Whole wheat toast","quantity":1,"measureLabel":"slice","nutrients":{"calories":80,"protein":4,"carbs":14,"fat":1,"fiber":2}}],"notes":"Adjusted portion size","nutritionTemplateId":null}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/NutritionLogDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Nutrition log updated successfully","data":{"id":"b3d2f1a0-5c6d-4e7f-8a9b-0c1d2e3f4a5b","userId":"c1d2e3f4-a5b6-7c8d-9e0f-1a2b3c4d5e6f","loggedBy":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","logDate":"2026-03-15","logTime":"08:30","inputType":"TEXT","originalInput":"2 eggs and a slice of toast","image":null,"items":[{"name":"Eggs","quantity":2,"measureLabel":"large","nutrients":{"calories":140,"protein":12,"carbs":1,"fat":10,"fiber":0}},{"name":"Whole wheat toast","quantity":1,"measureLabel":"slice","nutrients":{"calories":80,"protein":4,"carbs":14,"fat":1,"fiber":2}}],"totalNutrients":{"calories":220,"protein":16,"carbs":15,"fat":11,"fiber":2},"notes":null,"nutritionTemplateId":null,"createdAt":"2026-03-15T08:31:00.000Z","updatedAt":"2026-03-15T08:31:00.000Z"}}}},"description":"Success"}},"summary":"Update a nutrition log","tags":["Nutrition Tracking"]},"delete":{"operationId":"NutritionTrackingController.deleteLog","parameters":[{"in":"path","name":"logId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Nutrition log deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete a nutrition log","tags":["Nutrition Tracking"]}},"/v1/nutrition/utils/calculate-tdee":{"post":{"operationId":"NutritionUtilsController.calculateTDEE","requestBody":{"content":{"application/json":{"schema":{},"example":{"age":30,"weight":80,"height":180,"sex":"MALE","activityLevel":"MODERATELY_ACTIVE","includeMacros":true}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"TDEE calculated successfully","data":{"bmr":1780,"bmi":24.7,"bmiCategory":"Normal weight","tdee":2759,"activityLevel":"MODERATELY_ACTIVE","formula":"MIFFLIN_ST_JEOR","macroSuggestions":{"protein":{"min":128,"max":176},"carbs":{"min":250,"max":380},"fats":{"min":61,"max":107},"calories":2759},"weightGoalOptions":null}}}},"description":"Success"}},"summary":"Calculate TDEE, BMR, BMI and macro suggestions (pure computation)","tags":["Nutrition Utils"]}},"/v1/workout/exercises/":{"post":{"operationId":"ExerciseController.createExercise","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExerciseDto"},"example":{"name":"Barbell Back Squat","difficultyLevel":"INTERMEDIATE","exerciseType":"STRENGTH","status":"ACTIVE","isCompound":true,"riskLevel":2,"primaryMuscleGroups":["THIGHS","HIPS"],"equipmentRequired":["FULL_GYM"],"isPublicLibrary":false}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ExerciseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Exercise created successfully","data":{"id":"7c1d4e9a-2f3b-4a6c-8d5e-9f0a1b2c3d4e","createdAt":"2026-01-10T08:00:00.000Z","updatedAt":"2026-02-01T14:30:00.000Z","createdBy":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","name":"Barbell Back Squat","difficultyLevel":"INTERMEDIATE","exerciseType":"STRENGTH","status":"ACTIVE","isCompound":true,"riskLevel":2,"primaryMuscleGroups":["THIGHS","HIPS"],"equipmentRequired":["FULL_GYM"],"isPublicLibrary":true,"alternatives":["9b8a7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d"]}}}},"description":"Success"}},"summary":"Create an exercise","tags":["Exercise"]},"get":{"operationId":"ExerciseController.listExercises","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Exercises retrieved successfully","data":{"items":[{"id":"7c1d4e9a-2f3b-4a6c-8d5e-9f0a1b2c3d4e","createdAt":"2026-01-10T08:00:00.000Z","updatedAt":"2026-02-01T14:30:00.000Z","createdBy":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","name":"Barbell Back Squat","difficultyLevel":"INTERMEDIATE","exerciseType":"STRENGTH","status":"ACTIVE","isCompound":true,"riskLevel":2,"primaryMuscleGroups":["THIGHS","HIPS"],"equipmentRequired":["FULL_GYM"],"isPublicLibrary":true,"alternatives":["9b8a7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d"]}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List exercises (paginated, filterable)","tags":["Exercise"]}},"/v1/workout/exercises/filters":{"get":{"operationId":"ExerciseController.getExerciseFilterOptions","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ExerciseFiltersResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Exercise filter options retrieved successfully","data":{"exerciseTypes":["STRENGTH","CARDIO","FLEXIBILITY"],"difficultyLevels":["BEGINNER","INTERMEDIATE","ADVANCED"],"muscleGroups":["CHEST","BACK","THIGHS"],"equipmentOptions":["NONE","MINIMAL","FULL_GYM"],"statusOptions":["DRAFT","ACTIVE","DEPRECATED"]}}}},"description":"Success"}},"summary":"Get available exercise filter options","tags":["Exercise"]}},"/v1/workout/exercises/generate":{"post":{"operationId":"ExerciseController.generateExercise","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateExerciseRequestDto"},"example":{"descriptionQuery":"A hamstring-focused hinge I can do with dumbbells at home"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/GenerateExerciseResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Exercise generated successfully","data":{"response":"Here is a dumbbell Romanian deadlift tailored to your setup.","generatedExercise":{"name":"Dumbbell Romanian Deadlift","difficultyLevel":"BEGINNER","exerciseType":"STRENGTH","status":"DRAFT","isCompound":true,"primaryMuscleGroups":["THIGHS"],"equipmentRequired":["MINIMAL"],"isPublicLibrary":false}}}}},"description":"Success"}},"summary":"Generate an exercise from a natural-language description (AI)","tags":["Exercise"]}},"/v1/workout/exercises/{exerciseId}/alternatives":{"post":{"operationId":"ExerciseController.findAlternativeExercises","parameters":[{"in":"path","name":"exerciseId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FindAlternativesRequestDto"},"example":{"equipmentAvailable":["MINIMAL","HOME_GYM"]}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"type":"array","items":{"$ref":"#/components/schemas/ExerciseDto"}}},"required":["success","message","data"]},"example":{"success":true,"message":"Alternative exercises retrieved successfully","data":[{"id":"7c1d4e9a-2f3b-4a6c-8d5e-9f0a1b2c3d4e","createdAt":"2026-01-10T08:00:00.000Z","updatedAt":"2026-02-01T14:30:00.000Z","createdBy":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","name":"Barbell Back Squat","difficultyLevel":"INTERMEDIATE","exerciseType":"STRENGTH","status":"ACTIVE","isCompound":true,"riskLevel":2,"primaryMuscleGroups":["THIGHS","HIPS"],"equipmentRequired":["FULL_GYM"],"isPublicLibrary":true,"alternatives":["9b8a7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d"]}]}}},"description":"Success"}},"summary":"Find alternative exercises for a given exercise","tags":["Exercise"]}},"/v1/workout/exercises/{exerciseId}":{"get":{"operationId":"ExerciseController.getExerciseById","parameters":[{"in":"path","name":"exerciseId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ExerciseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Exercise retrieved successfully","data":{"id":"7c1d4e9a-2f3b-4a6c-8d5e-9f0a1b2c3d4e","createdAt":"2026-01-10T08:00:00.000Z","updatedAt":"2026-02-01T14:30:00.000Z","createdBy":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","name":"Barbell Back Squat","difficultyLevel":"INTERMEDIATE","exerciseType":"STRENGTH","status":"ACTIVE","isCompound":true,"riskLevel":2,"primaryMuscleGroups":["THIGHS","HIPS"],"equipmentRequired":["FULL_GYM"],"isPublicLibrary":true,"alternatives":["9b8a7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d"]}}}},"description":"Success"}},"summary":"Get an exercise by id","tags":["Exercise"]},"put":{"operationId":"ExerciseController.updateExercise","parameters":[{"in":"path","name":"exerciseId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExerciseDto"},"example":{"id":"7c1d4e9a-2f3b-4a6c-8d5e-9f0a1b2c3d4e","createdAt":"2026-01-10T08:00:00.000Z","updatedAt":"2026-02-01T14:30:00.000Z","createdBy":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","name":"Barbell Back Squat","difficultyLevel":"INTERMEDIATE","exerciseType":"STRENGTH","status":"ACTIVE","isCompound":true,"riskLevel":2,"primaryMuscleGroups":["THIGHS","HIPS"],"equipmentRequired":["FULL_GYM"],"isPublicLibrary":true,"alternatives":["9b8a7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d"]}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ExerciseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Exercise updated successfully","data":{"id":"7c1d4e9a-2f3b-4a6c-8d5e-9f0a1b2c3d4e","createdAt":"2026-01-10T08:00:00.000Z","updatedAt":"2026-02-01T14:30:00.000Z","createdBy":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","name":"Barbell Back Squat","difficultyLevel":"INTERMEDIATE","exerciseType":"STRENGTH","status":"ACTIVE","isCompound":true,"riskLevel":2,"primaryMuscleGroups":["THIGHS","HIPS"],"equipmentRequired":["FULL_GYM"],"isPublicLibrary":true,"alternatives":["9b8a7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d"]}}}},"description":"Success"}},"summary":"Update an exercise","tags":["Exercise"]},"delete":{"operationId":"ExerciseController.deleteExercise","parameters":[{"in":"path","name":"exerciseId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Exercise deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete (soft) an exercise","tags":["Exercise"]}},"/v1/workout/":{"post":{"operationId":"WorkoutController.createWorkout","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkoutDto"},"example":{"id":null,"createdAt":null,"updatedAt":null,"name":"Full Body Strength A","description":null,"image":null,"noImage":null,"durationMinutes":60,"difficulty":"MODERATE","unitSystem":"METRIC","experienceLevel":"INTERMEDIATE","energyRequirement":3,"goal":"MUSCLE_GAIN","caloriesBurnedEstimate":450,"isTemplate":true,"isPublicLibrary":false,"frequencyPerWeek":3,"userId":null,"assignedBy":null,"scheduledDate":null,"muscleGroups":["CHEST","BACK","THIGHS"],"equipmentRequired":["FULL_GYM"],"tags":["strength","full-body"],"exerciseGroups":[{"id":"c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f","name":"Main","format":"REGULAR","exercises":[{"id":"d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f80","exerciseId":"7c1d4e9a-2f3b-4a6c-8d5e-9f0a1b2c3d4e","name":"Barbell Back Squat","sets":4,"repRule":"8","restAfterSeconds":120,"section":"MAIN"}],"section":"MAIN"}],"timeCapMinutes":null,"targetRounds":null,"restBetweenRoundsSeconds":null,"scalingOptions":null,"runningDistanceMeters":null,"totalStations":null,"tenantId":null,"translations":null,"ownerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","visibility":null,"categoryId":null,"externalId":null,"externalUpdatedAt":null,"templateMode":"ADVANCED","metadata":null}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/WorkoutDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Workout created successfully","data":{"id":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","createdAt":"2026-01-12T09:00:00.000Z","updatedAt":"2026-02-03T11:15:00.000Z","name":"Full Body Strength A","description":null,"image":null,"noImage":null,"durationMinutes":60,"difficulty":"MODERATE","unitSystem":"METRIC","experienceLevel":"INTERMEDIATE","energyRequirement":3,"goal":"MUSCLE_GAIN","caloriesBurnedEstimate":450,"isTemplate":true,"isPublicLibrary":false,"frequencyPerWeek":3,"userId":null,"assignedBy":null,"scheduledDate":null,"muscleGroups":["CHEST","BACK","THIGHS"],"equipmentRequired":["FULL_GYM"],"tags":["strength","full-body"],"exerciseGroups":[{"id":"c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f","name":"Main","format":"REGULAR","exercises":[{"id":"d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f80","exerciseId":"7c1d4e9a-2f3b-4a6c-8d5e-9f0a1b2c3d4e","name":"Barbell Back Squat","sets":4,"repRule":"8","restAfterSeconds":120,"section":"MAIN"}],"section":"MAIN"}],"timeCapMinutes":null,"targetRounds":null,"restBetweenRoundsSeconds":null,"scalingOptions":null,"runningDistanceMeters":null,"totalStations":null,"tenantId":null,"translations":null,"ownerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","visibility":null,"categoryId":null,"externalId":null,"externalUpdatedAt":null,"templateMode":"ADVANCED","metadata":null}}}},"description":"Success"}},"summary":"Create a workout","tags":["Workout"]},"get":{"operationId":"WorkoutController.listWorkouts","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Workouts retrieved successfully","data":{"items":[{"id":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","createdAt":"2026-01-12T09:00:00.000Z","updatedAt":"2026-02-03T11:15:00.000Z","name":"Full Body Strength A","description":null,"image":null,"noImage":null,"durationMinutes":60,"difficulty":"MODERATE","unitSystem":"METRIC","experienceLevel":"INTERMEDIATE","energyRequirement":3,"goal":"MUSCLE_GAIN","caloriesBurnedEstimate":450,"isTemplate":true,"isPublicLibrary":false,"frequencyPerWeek":3,"userId":null,"assignedBy":null,"scheduledDate":null,"muscleGroups":["CHEST","BACK","THIGHS"],"equipmentRequired":["FULL_GYM"],"tags":["strength","full-body"],"exerciseGroups":[{"id":"c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f","name":"Main","format":"REGULAR","exercises":[{"id":"d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f80","exerciseId":"7c1d4e9a-2f3b-4a6c-8d5e-9f0a1b2c3d4e","name":"Barbell Back Squat","sets":4,"repRule":"8","restAfterSeconds":120,"section":"MAIN"}],"section":"MAIN"}],"timeCapMinutes":null,"targetRounds":null,"restBetweenRoundsSeconds":null,"scalingOptions":null,"runningDistanceMeters":null,"totalStations":null,"tenantId":null,"translations":null,"ownerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","visibility":null,"categoryId":null,"externalId":null,"externalUpdatedAt":null,"templateMode":"ADVANCED","metadata":null}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List workouts (paginated, filterable)","tags":["Workout"]}},"/v1/workout/{workoutId}/task":{"post":{"operationId":"WorkoutController.createTask","parameters":[{"in":"path","name":"workoutId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{}},"description":"Successful response"},"501":{"description":"Deferred: the LLM workout-generation task layer is not wired in this backend yet.","content":{"application/json":{"example":{"success":false,"message":"Workout AI generation is not yet migrated","data":null}}}}},"summary":"Start AI workout generation (not yet migrated)","tags":["Workout"],"description":"Deferred: the LLM workout-generation task layer is not wired in this backend yet."},"get":{"operationId":"WorkoutController.getTask","parameters":[{"in":"path","name":"workoutId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{}},"description":"Successful response"},"501":{"description":"Deferred: the scheduled-task state for the AI generation job is not wired in this backend yet.","content":{"application/json":{"example":{"success":false,"message":"Workout AI generation status is not yet migrated","data":null}}}}},"summary":"Get AI workout generation status (not yet migrated)","tags":["Workout"],"description":"Deferred: the scheduled-task state for the AI generation job is not wired in this backend yet."},"delete":{"operationId":"WorkoutController.deleteTask","parameters":[{"in":"path","name":"workoutId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{}},"description":"Successful response"},"501":{"description":"Deferred: AI generation task deletion is not wired in this backend yet.","content":{"application/json":{"example":{"success":false,"message":"Workout AI generation task deletion is not yet migrated","data":null}}}}},"summary":"Delete AI workout generation task (not yet migrated)","tags":["Workout"],"description":"Deferred: AI generation task deletion is not wired in this backend yet."}},"/v1/workout/{workoutId}/suggestions":{"post":{"operationId":"WorkoutController.getSuggestions","parameters":[{"in":"path","name":"workoutId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{}},"description":"Successful response"},"501":{"description":"Deferred: the nano-model suggestion service is not wired in this backend yet.","content":{"application/json":{"example":{"success":false,"message":"Workout suggestions are not yet migrated","data":null}}}}},"summary":"Get AI workout-builder exercise suggestions (not yet migrated)","tags":["Workout"],"description":"Deferred: the nano-model suggestion service is not wired in this backend yet."}},"/v1/workout/{workoutId}/import":{"post":{"operationId":"WorkoutController.importWorkout","parameters":[{"in":"path","name":"workoutId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/WorkoutDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Workout imported to your library","data":{"id":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","createdAt":"2026-01-12T09:00:00.000Z","updatedAt":"2026-02-03T11:15:00.000Z","name":"Full Body Strength A","description":null,"image":null,"noImage":null,"durationMinutes":60,"difficulty":"MODERATE","unitSystem":"METRIC","experienceLevel":"INTERMEDIATE","energyRequirement":3,"goal":"MUSCLE_GAIN","caloriesBurnedEstimate":450,"isTemplate":true,"isPublicLibrary":false,"frequencyPerWeek":3,"userId":null,"assignedBy":null,"scheduledDate":null,"muscleGroups":["CHEST","BACK","THIGHS"],"equipmentRequired":["FULL_GYM"],"tags":["strength","full-body"],"exerciseGroups":[{"id":"c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f","name":"Main","format":"REGULAR","exercises":[{"id":"d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f80","exerciseId":"7c1d4e9a-2f3b-4a6c-8d5e-9f0a1b2c3d4e","name":"Barbell Back Squat","sets":4,"repRule":"8","restAfterSeconds":120,"section":"MAIN"}],"section":"MAIN"}],"timeCapMinutes":null,"targetRounds":null,"restBetweenRoundsSeconds":null,"scalingOptions":null,"runningDistanceMeters":null,"totalStations":null,"tenantId":null,"translations":null,"ownerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","visibility":null,"categoryId":null,"externalId":null,"externalUpdatedAt":null,"templateMode":"ADVANCED","metadata":null}}}},"description":"Success"}},"summary":"Import a viewable workout into your private library","tags":["Workout"]}},"/v1/workout/{workoutId}":{"get":{"operationId":"WorkoutController.getWorkoutById","parameters":[{"in":"path","name":"workoutId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/WorkoutDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Workout retrieved successfully","data":{"id":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","createdAt":"2026-01-12T09:00:00.000Z","updatedAt":"2026-02-03T11:15:00.000Z","name":"Full Body Strength A","description":null,"image":null,"noImage":null,"durationMinutes":60,"difficulty":"MODERATE","unitSystem":"METRIC","experienceLevel":"INTERMEDIATE","energyRequirement":3,"goal":"MUSCLE_GAIN","caloriesBurnedEstimate":450,"isTemplate":true,"isPublicLibrary":false,"frequencyPerWeek":3,"userId":null,"assignedBy":null,"scheduledDate":null,"muscleGroups":["CHEST","BACK","THIGHS"],"equipmentRequired":["FULL_GYM"],"tags":["strength","full-body"],"exerciseGroups":[{"id":"c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f","name":"Main","format":"REGULAR","exercises":[{"id":"d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f80","exerciseId":"7c1d4e9a-2f3b-4a6c-8d5e-9f0a1b2c3d4e","name":"Barbell Back Squat","sets":4,"repRule":"8","restAfterSeconds":120,"section":"MAIN"}],"section":"MAIN"}],"timeCapMinutes":null,"targetRounds":null,"restBetweenRoundsSeconds":null,"scalingOptions":null,"runningDistanceMeters":null,"totalStations":null,"tenantId":null,"translations":null,"ownerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","visibility":null,"categoryId":null,"externalId":null,"externalUpdatedAt":null,"templateMode":"ADVANCED","metadata":null}}}},"description":"Success"}},"summary":"Get a workout by id","tags":["Workout"]},"put":{"operationId":"WorkoutController.updateWorkout","parameters":[{"in":"path","name":"workoutId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkoutDto"},"example":{"id":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","createdAt":"2026-01-12T09:00:00.000Z","updatedAt":"2026-02-03T11:15:00.000Z","name":"Full Body Strength A","description":null,"image":null,"noImage":null,"durationMinutes":60,"difficulty":"MODERATE","unitSystem":"METRIC","experienceLevel":"INTERMEDIATE","energyRequirement":3,"goal":"MUSCLE_GAIN","caloriesBurnedEstimate":450,"isTemplate":true,"isPublicLibrary":false,"frequencyPerWeek":3,"userId":null,"assignedBy":null,"scheduledDate":null,"muscleGroups":["CHEST","BACK","THIGHS"],"equipmentRequired":["FULL_GYM"],"tags":["strength","full-body"],"exerciseGroups":[{"id":"c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f","name":"Main","format":"REGULAR","exercises":[{"id":"d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f80","exerciseId":"7c1d4e9a-2f3b-4a6c-8d5e-9f0a1b2c3d4e","name":"Barbell Back Squat","sets":4,"repRule":"8","restAfterSeconds":120,"section":"MAIN"}],"section":"MAIN"}],"timeCapMinutes":null,"targetRounds":null,"restBetweenRoundsSeconds":null,"scalingOptions":null,"runningDistanceMeters":null,"totalStations":null,"tenantId":null,"translations":null,"ownerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","visibility":null,"categoryId":null,"externalId":null,"externalUpdatedAt":null,"templateMode":"ADVANCED","metadata":null}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/WorkoutDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Workout updated successfully","data":{"id":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","createdAt":"2026-01-12T09:00:00.000Z","updatedAt":"2026-02-03T11:15:00.000Z","name":"Full Body Strength A","description":null,"image":null,"noImage":null,"durationMinutes":60,"difficulty":"MODERATE","unitSystem":"METRIC","experienceLevel":"INTERMEDIATE","energyRequirement":3,"goal":"MUSCLE_GAIN","caloriesBurnedEstimate":450,"isTemplate":true,"isPublicLibrary":false,"frequencyPerWeek":3,"userId":null,"assignedBy":null,"scheduledDate":null,"muscleGroups":["CHEST","BACK","THIGHS"],"equipmentRequired":["FULL_GYM"],"tags":["strength","full-body"],"exerciseGroups":[{"id":"c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f","name":"Main","format":"REGULAR","exercises":[{"id":"d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f80","exerciseId":"7c1d4e9a-2f3b-4a6c-8d5e-9f0a1b2c3d4e","name":"Barbell Back Squat","sets":4,"repRule":"8","restAfterSeconds":120,"section":"MAIN"}],"section":"MAIN"}],"timeCapMinutes":null,"targetRounds":null,"restBetweenRoundsSeconds":null,"scalingOptions":null,"runningDistanceMeters":null,"totalStations":null,"tenantId":null,"translations":null,"ownerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","visibility":null,"categoryId":null,"externalId":null,"externalUpdatedAt":null,"templateMode":"ADVANCED","metadata":null}}}},"description":"Success"}},"summary":"Update a workout","tags":["Workout"]},"delete":{"operationId":"WorkoutController.deleteWorkout","parameters":[{"in":"path","name":"workoutId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Workout deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete a workout","tags":["Workout"]}},"/v1/workoutTracking/sessions/start":{"post":{"operationId":"WorkoutTrackingController.startWorkoutSession","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartWorkoutSessionRequestDto"},"example":{"clientSessionId":"f6a7b8c9-d0e1-4f2a-3b4c-5d6e7f809112","workoutId":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","isCustom":false}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/WorkoutSessionDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Workout session started successfully","data":{"id":"e5f6a7b8-c9d0-4e1f-2a3b-4c5d6e7f8091","createdAt":"2026-02-05T07:30:00.000Z","updatedAt":"2026-02-05T08:35:00.000Z","userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","workoutId":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","workoutName":"Full Body Strength A","isCustom":false,"customWorkoutName":null,"startedAt":"2026-02-05T07:30:00.000Z","completedAt":"2026-02-05T08:35:00.000Z","durationMinutes":65,"status":"COMPLETED","difficultyRating":4,"enjoymentRating":5,"energyLevelBefore":3,"energyLevelAfter":4,"notes":"Felt strong on squats, added 5kg.","caloriesBurnedEstimate":480,"exercisesCompleted":[{"exerciseId":"7c1d4e9a-2f3b-4a6c-8d5e-9f0a1b2c3d4e","exerciseName":"Barbell Back Squat","order":1,"units":["reps","kg"],"completedSets":[{"setNumber":1,"values":[8,100],"rpe":8,"completed":true},{"setNumber":2,"values":[8,100],"rpe":8,"completed":true}]}],"clientSessionId":"f6a7b8c9-d0e1-4f2a-3b4c-5d6e7f809112"}}}},"description":"Success"}},"summary":"Start a workout session","tags":["Workout Tracking"]}},"/v1/workoutTracking/sessions":{"post":{"operationId":"WorkoutTrackingController.createWorkoutSession","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWorkoutSessionRequestDto"},"example":{"clientSessionId":"f6a7b8c9-d0e1-4f2a-3b4c-5d6e7f809112","workoutId":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","status":"IN_PROGRESS","energyLevelBefore":3,"exercisesCompleted":[{"exerciseId":"7c1d4e9a-2f3b-4a6c-8d5e-9f0a1b2c3d4e","exerciseName":"Barbell Back Squat","order":1,"units":["reps","kg"],"completedSets":[{"setNumber":1,"values":[8,100],"rpe":8,"completed":true}]}]}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/WorkoutSessionDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Workout session created successfully","data":{"id":"e5f6a7b8-c9d0-4e1f-2a3b-4c5d6e7f8091","createdAt":"2026-02-05T07:30:00.000Z","updatedAt":"2026-02-05T08:35:00.000Z","userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","workoutId":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","workoutName":"Full Body Strength A","isCustom":false,"customWorkoutName":null,"startedAt":"2026-02-05T07:30:00.000Z","completedAt":"2026-02-05T08:35:00.000Z","durationMinutes":65,"status":"COMPLETED","difficultyRating":4,"enjoymentRating":5,"energyLevelBefore":3,"energyLevelAfter":4,"notes":"Felt strong on squats, added 5kg.","caloriesBurnedEstimate":480,"exercisesCompleted":[{"exerciseId":"7c1d4e9a-2f3b-4a6c-8d5e-9f0a1b2c3d4e","exerciseName":"Barbell Back Squat","order":1,"units":["reps","kg"],"completedSets":[{"setNumber":1,"values":[8,100],"rpe":8,"completed":true},{"setNumber":2,"values":[8,100],"rpe":8,"completed":true}]}],"clientSessionId":"f6a7b8c9-d0e1-4f2a-3b4c-5d6e7f809112"}}}},"description":"Success"}},"summary":"Create a workout session with full data","tags":["Workout Tracking"]},"get":{"operationId":"WorkoutTrackingController.listUserWorkoutSessions","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Workout sessions retrieved successfully","data":{"items":[{"id":"e5f6a7b8-c9d0-4e1f-2a3b-4c5d6e7f8091","createdAt":"2026-02-05T07:30:00.000Z","updatedAt":"2026-02-05T08:35:00.000Z","userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","workoutId":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","workoutName":"Full Body Strength A","isCustom":false,"customWorkoutName":null,"startedAt":"2026-02-05T07:30:00.000Z","completedAt":"2026-02-05T08:35:00.000Z","durationMinutes":65,"status":"COMPLETED","difficultyRating":4,"enjoymentRating":5,"energyLevelBefore":3,"energyLevelAfter":4,"notes":"Felt strong on squats, added 5kg.","caloriesBurnedEstimate":480,"exercisesCompleted":[{"exerciseId":"7c1d4e9a-2f3b-4a6c-8d5e-9f0a1b2c3d4e","exerciseName":"Barbell Back Squat","order":1,"units":["reps","kg"],"completedSets":[{"setNumber":1,"values":[8,100],"rpe":8,"completed":true},{"setNumber":2,"values":[8,100],"rpe":8,"completed":true}]}],"clientSessionId":"f6a7b8c9-d0e1-4f2a-3b4c-5d6e7f809112"}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List a user's workout sessions (paginated, filterable)","tags":["Workout Tracking"]}},"/v1/workoutTracking/sessionsSummary":{"get":{"operationId":"WorkoutTrackingController.getUserWorkoutSummary","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/WorkoutSessionSummaryDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Workout summary retrieved successfully","data":{"userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","startDate":"2026-01-01T00:00:00.000Z","endDate":"2026-01-31T23:59:59.000Z","totalSessions":12,"totalDurationMinutes":780,"totalCaloriesBurned":5400,"mostTrainedMuscleGroups":["THIGHS","BACK","CHEST"],"averageDifficultyRating":4,"averageEnjoymentRating":4,"completionRate":0.92,"performanceTrends":{}}}}},"description":"Success"}},"summary":"Get a workout summary for a user over a date range","tags":["Workout Tracking"]}},"/v1/workoutTracking/exerciseHistory":{"get":{"operationId":"WorkoutTrackingController.getExerciseHistoryBulk","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Exercise history retrieved successfully","data":{"items":[{"id":"a7b8c9d0-e1f2-4a3b-4c5d-6e7f80911223","exerciseId":"7c1d4e9a-2f3b-4a6c-8d5e-9f0a1b2c3d4e","exerciseName":"Barbell Back Squat","sessionId":"e5f6a7b8-c9d0-4e1f-2a3b-4c5d6e7f8091","workoutId":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","workoutName":"Full Body Strength A","completedAt":"2026-02-05T08:00:00.000Z","units":["reps","kg"],"completedSets":[{"setNumber":1,"values":[8,100],"rpe":8,"completed":true},{"setNumber":2,"values":[8,100],"rpe":8,"completed":true}],"rpeAvg":8,"restTimeAvgSeconds":120}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"Get exercise history for multiple exercises (paginated)","tags":["Workout Tracking"]}},"/v1/workoutTracking/exercises/{exerciseId}/history":{"get":{"operationId":"WorkoutTrackingController.getExerciseHistory","parameters":[{"in":"path","name":"exerciseId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Exercise history retrieved successfully","data":{"items":[{"id":"a7b8c9d0-e1f2-4a3b-4c5d-6e7f80911223","exerciseId":"7c1d4e9a-2f3b-4a6c-8d5e-9f0a1b2c3d4e","exerciseName":"Barbell Back Squat","sessionId":"e5f6a7b8-c9d0-4e1f-2a3b-4c5d6e7f8091","workoutId":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","workoutName":"Full Body Strength A","completedAt":"2026-02-05T08:00:00.000Z","units":["reps","kg"],"completedSets":[{"setNumber":1,"values":[8,100],"rpe":8,"completed":true},{"setNumber":2,"values":[8,100],"rpe":8,"completed":true}],"rpeAvg":8,"restTimeAvgSeconds":120}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"Get history for a single exercise (paginated)","tags":["Workout Tracking"]}},"/v1/workoutTracking/sessions/{clientSessionId}":{"put":{"operationId":"WorkoutTrackingController.updateWorkoutSession","parameters":[{"in":"path","name":"clientSessionId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWorkoutSessionRequestDto"},"example":{"status":"COMPLETED","difficultyRating":4,"enjoymentRating":5,"energyLevelAfter":4,"notes":"Great session, hit all target reps."}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/WorkoutSessionDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Workout session updated successfully","data":{"id":"e5f6a7b8-c9d0-4e1f-2a3b-4c5d6e7f8091","createdAt":"2026-02-05T07:30:00.000Z","updatedAt":"2026-02-05T08:35:00.000Z","userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","workoutId":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","workoutName":"Full Body Strength A","isCustom":false,"customWorkoutName":null,"startedAt":"2026-02-05T07:30:00.000Z","completedAt":"2026-02-05T08:35:00.000Z","durationMinutes":65,"status":"COMPLETED","difficultyRating":4,"enjoymentRating":5,"energyLevelBefore":3,"energyLevelAfter":4,"notes":"Felt strong on squats, added 5kg.","caloriesBurnedEstimate":480,"exercisesCompleted":[{"exerciseId":"7c1d4e9a-2f3b-4a6c-8d5e-9f0a1b2c3d4e","exerciseName":"Barbell Back Squat","order":1,"units":["reps","kg"],"completedSets":[{"setNumber":1,"values":[8,100],"rpe":8,"completed":true},{"setNumber":2,"values":[8,100],"rpe":8,"completed":true}]}],"clientSessionId":"f6a7b8c9-d0e1-4f2a-3b4c-5d6e7f809112"}}}},"description":"Success"}},"summary":"Update a workout session","tags":["Workout Tracking"]},"get":{"operationId":"WorkoutTrackingController.getWorkoutSessionById","parameters":[{"in":"path","name":"clientSessionId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/WorkoutSessionDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Workout session retrieved successfully","data":{"id":"e5f6a7b8-c9d0-4e1f-2a3b-4c5d6e7f8091","createdAt":"2026-02-05T07:30:00.000Z","updatedAt":"2026-02-05T08:35:00.000Z","userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","workoutId":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","workoutName":"Full Body Strength A","isCustom":false,"customWorkoutName":null,"startedAt":"2026-02-05T07:30:00.000Z","completedAt":"2026-02-05T08:35:00.000Z","durationMinutes":65,"status":"COMPLETED","difficultyRating":4,"enjoymentRating":5,"energyLevelBefore":3,"energyLevelAfter":4,"notes":"Felt strong on squats, added 5kg.","caloriesBurnedEstimate":480,"exercisesCompleted":[{"exerciseId":"7c1d4e9a-2f3b-4a6c-8d5e-9f0a1b2c3d4e","exerciseName":"Barbell Back Squat","order":1,"units":["reps","kg"],"completedSets":[{"setNumber":1,"values":[8,100],"rpe":8,"completed":true},{"setNumber":2,"values":[8,100],"rpe":8,"completed":true}]}],"clientSessionId":"f6a7b8c9-d0e1-4f2a-3b4c-5d6e7f809112"}}}},"description":"Success"}},"summary":"Get a workout session by client session id","tags":["Workout Tracking"]}},"/v1/programs/":{"post":{"operationId":"ProgramController.createProgram","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProgramRequestDto"},"example":{"name":"12-Week Strength Foundations","description":"A three-phase strength program for intermediate lifters.","isTemplate":true,"programType":"FULL","planningType":"PHASED","totalDurationWeeks":12,"programGoal":"Build a strength base","difficultyLevel":"INTERMEDIATE","status":"PENDING"}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProgramDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Program created successfully","data":{"id":"c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f","createdAt":"2026-01-05T10:00:00.000Z","updatedAt":"2026-02-01T09:00:00.000Z","name":"12-Week Strength Foundations","description":"A three-phase strength program for intermediate lifters.","userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","createdById":"d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f80","phases":null,"startDate":"2026-02-10","endDate":"2026-05-04","pausedAt":null,"resumeAt":null,"stagedRollout":false,"rolloutMaxLookupWeeks":null,"rolloutIntervalWeeks":null,"isTemplate":true,"isPublicLibrary":false,"isActive":false,"status":"PENDING","totalDurationWeeks":12,"programGoal":"Build a strength base","difficultyLevel":"INTERMEDIATE","tags":"strength,foundations","planningType":"PHASED","programType":"FULL","sections":["WORKOUT","NUTRITION"],"nutritionPlanningType":"AVERAGE","workoutPlanningOptions":["WORKOUTS"],"contentCollections":null,"image":null,"video":null,"daysPreviewType":"DAY_NUMBER","externalId":null,"externalUpdatedAt":null,"ownerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","visibility":null,"categoryId":null,"templateId":null,"duplicateContentProgramId":null,"placeholderValues":null,"purchaseId":null,"purchase":null,"externalPurchaseId":null,"position":0,"currentWeek":null,"totalWeeks":12,"daysUntilEnd":null}}}},"description":"Success"}},"summary":"Create a program","tags":["Program"]},"get":{"operationId":"ProgramController.listPrograms","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Programs retrieved successfully","data":{"items":[{"id":"c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f","createdAt":"2026-01-05T10:00:00.000Z","updatedAt":"2026-02-01T09:00:00.000Z","name":"12-Week Strength Foundations","description":"A three-phase strength program for intermediate lifters.","userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","createdById":"d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f80","phases":null,"startDate":"2026-02-10","endDate":"2026-05-04","pausedAt":null,"resumeAt":null,"stagedRollout":false,"rolloutMaxLookupWeeks":null,"rolloutIntervalWeeks":null,"isTemplate":true,"isPublicLibrary":false,"isActive":false,"status":"PENDING","totalDurationWeeks":12,"programGoal":"Build a strength base","difficultyLevel":"INTERMEDIATE","tags":"strength,foundations","planningType":"PHASED","programType":"FULL","sections":["WORKOUT","NUTRITION"],"nutritionPlanningType":"AVERAGE","workoutPlanningOptions":["WORKOUTS"],"contentCollections":null,"image":null,"video":null,"daysPreviewType":"DAY_NUMBER","externalId":null,"externalUpdatedAt":null,"ownerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","visibility":null,"categoryId":null,"templateId":null,"duplicateContentProgramId":null,"placeholderValues":null,"purchaseId":null,"purchase":null,"externalPurchaseId":null,"position":0,"currentWeek":null,"totalWeeks":12,"daysUntilEnd":null}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List programs (paginated, filterable)","tags":["Program"]}},"/v1/programs/{programId}/phases/{phaseId}/duplicate":{"post":{"operationId":"ProgramController.duplicatePhase","parameters":[{"in":"path","name":"programId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"path","name":"phaseId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProgramDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Phase duplicated","data":{"id":"c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f","createdAt":"2026-01-05T10:00:00.000Z","updatedAt":"2026-02-01T09:00:00.000Z","name":"12-Week Strength Foundations","description":"A three-phase strength program for intermediate lifters.","userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","createdById":"d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f80","phases":null,"startDate":"2026-02-10","endDate":"2026-05-04","pausedAt":null,"resumeAt":null,"stagedRollout":false,"rolloutMaxLookupWeeks":null,"rolloutIntervalWeeks":null,"isTemplate":true,"isPublicLibrary":false,"isActive":false,"status":"PENDING","totalDurationWeeks":12,"programGoal":"Build a strength base","difficultyLevel":"INTERMEDIATE","tags":"strength,foundations","planningType":"PHASED","programType":"FULL","sections":["WORKOUT","NUTRITION"],"nutritionPlanningType":"AVERAGE","workoutPlanningOptions":["WORKOUTS"],"contentCollections":null,"image":null,"video":null,"daysPreviewType":"DAY_NUMBER","externalId":null,"externalUpdatedAt":null,"ownerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","visibility":null,"categoryId":null,"templateId":null,"duplicateContentProgramId":null,"placeholderValues":null,"purchaseId":null,"purchase":null,"externalPurchaseId":null,"position":0,"currentWeek":null,"totalWeeks":12,"daysUntilEnd":null}}}},"description":"Success"}},"summary":"Duplicate a phase within a program","tags":["Program"]}},"/v1/programs/{programId}/unlink":{"put":{"operationId":"ProgramController.unlinkFromTemplate","parameters":[{"in":"path","name":"programId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProgramDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Program unlinked from template successfully","data":{"id":"c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f","createdAt":"2026-01-05T10:00:00.000Z","updatedAt":"2026-02-01T09:00:00.000Z","name":"12-Week Strength Foundations","description":"A three-phase strength program for intermediate lifters.","userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","createdById":"d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f80","phases":null,"startDate":"2026-02-10","endDate":"2026-05-04","pausedAt":null,"resumeAt":null,"stagedRollout":false,"rolloutMaxLookupWeeks":null,"rolloutIntervalWeeks":null,"isTemplate":true,"isPublicLibrary":false,"isActive":false,"status":"PENDING","totalDurationWeeks":12,"programGoal":"Build a strength base","difficultyLevel":"INTERMEDIATE","tags":"strength,foundations","planningType":"PHASED","programType":"FULL","sections":["WORKOUT","NUTRITION"],"nutritionPlanningType":"AVERAGE","workoutPlanningOptions":["WORKOUTS"],"contentCollections":null,"image":null,"video":null,"daysPreviewType":"DAY_NUMBER","externalId":null,"externalUpdatedAt":null,"ownerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","visibility":null,"categoryId":null,"templateId":null,"duplicateContentProgramId":null,"placeholderValues":null,"purchaseId":null,"purchase":null,"externalPurchaseId":null,"position":0,"currentWeek":null,"totalWeeks":12,"daysUntilEnd":null}}}},"description":"Success"}},"summary":"Unlink a program from its template","tags":["Program"]}},"/v1/programs/{programId}/import-phases":{"post":{"operationId":"ProgramController.importPhases","parameters":[{"in":"path","name":"programId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{},"example":{"phases":[{"id":"e5f6a7b8-c9d0-4e1f-2a3b-4c5d6e7f8091","name":"Foundation","phaseType":"FOUNDATION","description":"Weeks 1-4: build movement quality and base volume.","startDate":"2026-02-10","endDate":"2026-03-09","isActive":true,"status":"READY"}]}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Phases imported","data":[{"id":"e5f6a7b8-c9d0-4e1f-2a3b-4c5d6e7f8091","name":"Foundation","phaseType":"FOUNDATION","description":"Weeks 1-4: build movement quality and base volume.","startDate":"2026-02-10","endDate":"2026-03-09","isActive":true,"status":"READY"}]}}},"description":"Success"}},"summary":"Deep-copy source phases into a program","tags":["Program"]}},"/v1/programs/{programId}/activate":{"put":{"operationId":"ProgramController.activateProgram","parameters":[{"in":"path","name":"programId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProgramDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Program activated successfully","data":{"id":"c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f","createdAt":"2026-01-05T10:00:00.000Z","updatedAt":"2026-02-01T09:00:00.000Z","name":"12-Week Strength Foundations","description":"A three-phase strength program for intermediate lifters.","userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","createdById":"d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f80","phases":null,"startDate":"2026-02-10","endDate":"2026-05-04","pausedAt":null,"resumeAt":null,"stagedRollout":false,"rolloutMaxLookupWeeks":null,"rolloutIntervalWeeks":null,"isTemplate":true,"isPublicLibrary":false,"isActive":true,"status":"ACTIVE","totalDurationWeeks":12,"programGoal":"Build a strength base","difficultyLevel":"INTERMEDIATE","tags":"strength,foundations","planningType":"PHASED","programType":"FULL","sections":["WORKOUT","NUTRITION"],"nutritionPlanningType":"AVERAGE","workoutPlanningOptions":["WORKOUTS"],"contentCollections":null,"image":null,"video":null,"daysPreviewType":"DAY_NUMBER","externalId":null,"externalUpdatedAt":null,"ownerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","visibility":null,"categoryId":null,"templateId":null,"duplicateContentProgramId":null,"placeholderValues":null,"purchaseId":null,"purchase":null,"externalPurchaseId":null,"position":0,"currentWeek":null,"totalWeeks":12,"daysUntilEnd":null}}}},"description":"Success"}},"summary":"Activate a program","tags":["Program"]}},"/v1/programs/{programId}/move":{"put":{"operationId":"ProgramController.moveProgram","parameters":[{"in":"path","name":"programId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"query","name":"position","required":false,"schema":{"type":"number"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProgramDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Program moved successfully","data":{"id":"c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f","createdAt":"2026-01-05T10:00:00.000Z","updatedAt":"2026-02-01T09:00:00.000Z","name":"12-Week Strength Foundations","description":"A three-phase strength program for intermediate lifters.","userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","createdById":"d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f80","phases":null,"startDate":"2026-02-10","endDate":"2026-05-04","pausedAt":null,"resumeAt":null,"stagedRollout":false,"rolloutMaxLookupWeeks":null,"rolloutIntervalWeeks":null,"isTemplate":true,"isPublicLibrary":false,"isActive":false,"status":"PENDING","totalDurationWeeks":12,"programGoal":"Build a strength base","difficultyLevel":"INTERMEDIATE","tags":"strength,foundations","planningType":"PHASED","programType":"FULL","sections":["WORKOUT","NUTRITION"],"nutritionPlanningType":"AVERAGE","workoutPlanningOptions":["WORKOUTS"],"contentCollections":null,"image":null,"video":null,"daysPreviewType":"DAY_NUMBER","externalId":null,"externalUpdatedAt":null,"ownerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","visibility":null,"categoryId":null,"templateId":null,"duplicateContentProgramId":null,"placeholderValues":null,"purchaseId":null,"purchase":null,"externalPurchaseId":null,"position":3,"currentWeek":null,"totalWeeks":12,"daysUntilEnd":null}}}},"description":"Success"}},"summary":"Move a program to a new list position","tags":["Program"]}},"/v1/programs/{programId}/search":{"get":{"operationId":"ProgramController.searchInProgram","parameters":[{"in":"path","name":"programId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"query","name":"q","required":false,"schema":{"type":"string"}},{"in":"query","name":"types","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Search completed","data":{"query":"squat","totalMatches":2,"matches":[{"matchType":"EXERCISE_NAME","matchedText":"Barbell Back Squat","entityId":"7c1d4e9a-2f3b-4a6c-8d5e-9f0a1b2c3d4e","entityName":"Barbell Back Squat","phaseIndex":0,"phaseName":"Foundation"},{"matchType":"WORKOUT_NAME","matchedText":"Squat Focus Day","entityId":"b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e","entityName":"Squat Focus Day"}]}}}},"description":"Success"}},"summary":"Search within a program (workouts, nutrition, content)","tags":["Program"]}},"/v1/programs/{programId}/placeholders":{"get":{"operationId":"ProgramController.getProgramPlaceholders","parameters":[{"in":"path","name":"programId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Placeholders retrieved","data":{"placeholders":["clientName","startDate","targetCalories"]}}}},"description":"Success"}},"summary":"List distinct {{key}} placeholders in a program's nutrition content","tags":["Program"]}},"/v1/programs/{programId}/task":{"post":{"operationId":"ProgramController.createProgramTask","parameters":[{"in":"path","name":"programId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{}},"description":"Successful response"},"501":{"description":"Deferred: the LLM program-generation task layer is not wired in this backend yet.","content":{"application/json":{"example":{"success":false,"message":"Program AI generation is not yet migrated","data":null}}}}},"summary":"Start AI program generation (not yet migrated)","tags":["Program"],"description":"Deferred: the LLM program-generation task layer is not wired in this backend yet."},"get":{"operationId":"ProgramController.getProgramTask","parameters":[{"in":"path","name":"programId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{}},"description":"Successful response"},"501":{"description":"Deferred: the scheduled-task state for the AI generation job is not wired in this backend yet.","content":{"application/json":{"example":{"success":false,"message":"Program AI generation status is not yet migrated","data":null}}}}},"summary":"Get AI program generation status (not yet migrated)","tags":["Program"],"description":"Deferred: the scheduled-task state for the AI generation job is not wired in this backend yet."},"delete":{"operationId":"ProgramController.deleteProgramTask","parameters":[{"in":"path","name":"programId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{}},"description":"Successful response"},"501":{"description":"Deferred: AI generation task deletion is not wired in this backend yet.","content":{"application/json":{"example":{"success":false,"message":"Program AI generation task deletion is not yet migrated","data":null}}}}},"summary":"Delete AI program generation task (not yet migrated)","tags":["Program"],"description":"Deferred: AI generation task deletion is not wired in this backend yet."}},"/v1/programs/{programId}/export.pdf":{"get":{"operationId":"ProgramController.exportProgramPdf","parameters":[{"in":"path","name":"programId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{}},"description":"Returns the rendered PDF bytes (application/pdf) with Content-Disposition: attachment. When the renderer is at capacity, responds 503 with a Retry-After header."}},"summary":"Export a program as a PDF","tags":["Program"],"description":"Returns the rendered PDF bytes (application/pdf) with Content-Disposition: attachment. When the renderer is at capacity, responds 503 with a Retry-After header."}},"/v1/programs/{programId}":{"get":{"operationId":"ProgramController.getProgramById","parameters":[{"in":"path","name":"programId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProgramDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Program retrieved successfully","data":{"id":"c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f","createdAt":"2026-01-05T10:00:00.000Z","updatedAt":"2026-02-01T09:00:00.000Z","name":"12-Week Strength Foundations","description":"A three-phase strength program for intermediate lifters.","userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","createdById":"d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f80","phases":null,"startDate":"2026-02-10","endDate":"2026-05-04","pausedAt":null,"resumeAt":null,"stagedRollout":false,"rolloutMaxLookupWeeks":null,"rolloutIntervalWeeks":null,"isTemplate":true,"isPublicLibrary":false,"isActive":false,"status":"PENDING","totalDurationWeeks":12,"programGoal":"Build a strength base","difficultyLevel":"INTERMEDIATE","tags":"strength,foundations","planningType":"PHASED","programType":"FULL","sections":["WORKOUT","NUTRITION"],"nutritionPlanningType":"AVERAGE","workoutPlanningOptions":["WORKOUTS"],"contentCollections":null,"image":null,"video":null,"daysPreviewType":"DAY_NUMBER","externalId":null,"externalUpdatedAt":null,"ownerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","visibility":null,"categoryId":null,"templateId":null,"duplicateContentProgramId":null,"placeholderValues":null,"purchaseId":null,"purchase":null,"externalPurchaseId":null,"position":0,"currentWeek":null,"totalWeeks":12,"daysUntilEnd":null}}}},"description":"Success"}},"summary":"Get a program by id","tags":["Program"]},"put":{"operationId":"ProgramController.updateProgram","parameters":[{"in":"path","name":"programId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProgramRequestDto"},"example":{"name":"12-Week Strength Foundations","description":"A three-phase strength program for intermediate lifters.","isTemplate":true,"programType":"FULL","planningType":"PHASED","totalDurationWeeks":12,"programGoal":"Build a strength base","difficultyLevel":"INTERMEDIATE","status":"PENDING"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/ProgramDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Program updated successfully","data":{"id":"c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f","createdAt":"2026-01-05T10:00:00.000Z","updatedAt":"2026-02-01T09:00:00.000Z","name":"12-Week Strength Foundations","description":"A three-phase strength program for intermediate lifters.","userId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","createdById":"d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f80","phases":null,"startDate":"2026-02-10","endDate":"2026-05-04","pausedAt":null,"resumeAt":null,"stagedRollout":false,"rolloutMaxLookupWeeks":null,"rolloutIntervalWeeks":null,"isTemplate":true,"isPublicLibrary":false,"isActive":false,"status":"PENDING","totalDurationWeeks":12,"programGoal":"Build a strength base","difficultyLevel":"INTERMEDIATE","tags":"strength,foundations","planningType":"PHASED","programType":"FULL","sections":["WORKOUT","NUTRITION"],"nutritionPlanningType":"AVERAGE","workoutPlanningOptions":["WORKOUTS"],"contentCollections":null,"image":null,"video":null,"daysPreviewType":"DAY_NUMBER","externalId":null,"externalUpdatedAt":null,"ownerId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","visibility":null,"categoryId":null,"templateId":null,"duplicateContentProgramId":null,"placeholderValues":null,"purchaseId":null,"purchase":null,"externalPurchaseId":null,"position":0,"currentWeek":null,"totalWeeks":12,"daysUntilEnd":null}}}},"description":"Success"}},"summary":"Update a program","tags":["Program"]},"delete":{"operationId":"ProgramController.deleteProgram","parameters":[{"in":"path","name":"programId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Program deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete a program","tags":["Program"]}},"/v1/tenants/":{"post":{"operationId":"TenantController.createTenant","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantRequestDto"},"example":{"name":"Peak Performance Coaching"}}},"description":"","required":false},"responses":{"201":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TenantResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Tenant created successfully","data":{"id":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","name":"Peak Performance Coaching","code":"PEAK","users":[{"id":"c3d4e5f6-a7b8-9c0d-1e2f-3a4b5c6d7e8f","firstName":"Dijana","lastName":"Keri","email":"dijana@peakperformance.com","phoneNumber":"+15551234567","role":"TRAINER","isActive":true,"dataProfilesCreated":true}],"createdAt":"2026-01-10T09:30:00.000Z","updatedAt":"2026-01-15T14:00:00.000Z","tagline":"Train smarter, live longer","logo":null,"primaryBrandColor":"#3F51B5","secondaryBrandColor":"#a5aeda","monthlyClientTarget":25,"primaryBusinessGoal":"Grow to 50 active clients","businessOnboardingCompletedAt":"2026-01-11T10:00:00.000Z","cover":null,"videoReel":null,"description":"Premium 1:1 longevity and strength coaching.","labels":null,"isPubliclyVisible":true,"subscriptionStatus":"ACTIVE","billingInterval":"MONTH","subscriptionCurrentPeriodEnd":"2026-02-10T09:30:00.000Z","subscriptionCancelAtPeriodEnd":false,"subscriptionPlan":"PROTOCOL_PRO","seatCount":3,"currentSeatUsage":2,"includedClients":100,"includedSeats":3,"additionalSeats":0,"aiCreditsUsed":1250,"aiCreditOverageEnabled":false,"notificationConfig":null,"coachingProfile":null}}}},"description":"Success"}},"summary":"Create a tenant (ADMIN only)","tags":["Tenant"]},"get":{"operationId":"TenantController.listTenants","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Tenants retrieved successfully","data":{"items":[{"id":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","name":"Peak Performance Coaching","code":"PEAK","users":[{"id":"c3d4e5f6-a7b8-9c0d-1e2f-3a4b5c6d7e8f","firstName":"Dijana","lastName":"Keri","email":"dijana@peakperformance.com","phoneNumber":"+15551234567","role":"TRAINER","isActive":true,"dataProfilesCreated":true}],"createdAt":"2026-01-10T09:30:00.000Z","updatedAt":"2026-01-15T14:00:00.000Z","tagline":"Train smarter, live longer","logo":null,"primaryBrandColor":"#3F51B5","secondaryBrandColor":"#a5aeda","monthlyClientTarget":25,"primaryBusinessGoal":"Grow to 50 active clients","businessOnboardingCompletedAt":"2026-01-11T10:00:00.000Z","cover":null,"videoReel":null,"description":"Premium 1:1 longevity and strength coaching.","labels":null,"isPubliclyVisible":true,"subscriptionStatus":"ACTIVE","billingInterval":"MONTH","subscriptionCurrentPeriodEnd":"2026-02-10T09:30:00.000Z","subscriptionCancelAtPeriodEnd":false,"subscriptionPlan":"PROTOCOL_PRO","seatCount":3,"currentSeatUsage":2,"includedClients":100,"includedSeats":3,"additionalSeats":0,"aiCreditsUsed":1250,"aiCreditOverageEnabled":false,"notificationConfig":null,"coachingProfile":null}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List tenants (ADMIN only, paginated)","tags":["Tenant"]}},"/v1/tenants/{tenantId}":{"get":{"operationId":"TenantController.getTenantById","parameters":[{"in":"path","name":"tenantId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TenantResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Tenant retrieved successfully","data":{"id":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","name":"Peak Performance Coaching","code":"PEAK","users":[{"id":"c3d4e5f6-a7b8-9c0d-1e2f-3a4b5c6d7e8f","firstName":"Dijana","lastName":"Keri","email":"dijana@peakperformance.com","phoneNumber":"+15551234567","role":"TRAINER","isActive":true,"dataProfilesCreated":true}],"createdAt":"2026-01-10T09:30:00.000Z","updatedAt":"2026-01-15T14:00:00.000Z","tagline":"Train smarter, live longer","logo":null,"primaryBrandColor":"#3F51B5","secondaryBrandColor":"#a5aeda","monthlyClientTarget":25,"primaryBusinessGoal":"Grow to 50 active clients","businessOnboardingCompletedAt":"2026-01-11T10:00:00.000Z","cover":null,"videoReel":null,"description":"Premium 1:1 longevity and strength coaching.","labels":null,"isPubliclyVisible":true,"subscriptionStatus":"ACTIVE","billingInterval":"MONTH","subscriptionCurrentPeriodEnd":"2026-02-10T09:30:00.000Z","subscriptionCancelAtPeriodEnd":false,"subscriptionPlan":"PROTOCOL_PRO","seatCount":3,"currentSeatUsage":2,"includedClients":100,"includedSeats":3,"additionalSeats":0,"aiCreditsUsed":1250,"aiCreditOverageEnabled":false,"notificationConfig":null,"coachingProfile":null}}}},"description":"Success"}},"summary":"Get a tenant by id","tags":["Tenant"]},"put":{"operationId":"TenantController.updateTenant","parameters":[{"in":"path","name":"tenantId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantUpdateRequestDto"},"example":{"name":"Peak Performance Coaching","tagline":"Train smarter, live longer","primaryBrandColor":"#3F51B5","monthlyClientTarget":25,"isPubliclyVisible":true}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/TenantResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Tenant updated successfully","data":{"id":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","name":"Peak Performance Coaching","code":"PEAK","users":[{"id":"c3d4e5f6-a7b8-9c0d-1e2f-3a4b5c6d7e8f","firstName":"Dijana","lastName":"Keri","email":"dijana@peakperformance.com","phoneNumber":"+15551234567","role":"TRAINER","isActive":true,"dataProfilesCreated":true}],"createdAt":"2026-01-10T09:30:00.000Z","updatedAt":"2026-01-15T14:00:00.000Z","tagline":"Train smarter, live longer","logo":null,"primaryBrandColor":"#3F51B5","secondaryBrandColor":"#a5aeda","monthlyClientTarget":25,"primaryBusinessGoal":"Grow to 50 active clients","businessOnboardingCompletedAt":"2026-01-11T10:00:00.000Z","cover":null,"videoReel":null,"description":"Premium 1:1 longevity and strength coaching.","labels":null,"isPubliclyVisible":true,"subscriptionStatus":"ACTIVE","billingInterval":"MONTH","subscriptionCurrentPeriodEnd":"2026-02-10T09:30:00.000Z","subscriptionCancelAtPeriodEnd":false,"subscriptionPlan":"PROTOCOL_PRO","seatCount":3,"currentSeatUsage":2,"includedClients":100,"includedSeats":3,"additionalSeats":0,"aiCreditsUsed":1250,"aiCreditOverageEnabled":false,"notificationConfig":null,"coachingProfile":null}}}},"description":"Success"}},"summary":"Update a tenant","tags":["Tenant"]},"delete":{"operationId":"TenantController.deleteTenant","parameters":[{"in":"path","name":"tenantId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Tenant deleted successfully","data":null}}},"description":"Success"}},"summary":"Delete a tenant (ADMIN only)","tags":["Tenant"]}},"/v1/public/tenants/code/{code}":{"get":{"operationId":"PublicTenantController.getPublicTenantByCode","parameters":[{"in":"path","name":"code","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/PublicTenantResponseDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Tenant retrieved successfully","data":{"id":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","name":"Peak Performance Coaching","code":"PEAK","tagline":"Train smarter, live longer","logo":null,"primaryBrandColor":"#3F51B5","secondaryBrandColor":"#a5aeda","cover":null,"videoReel":null,"description":"Premium 1:1 longevity and strength coaching.","labels":null}}}},"description":"Success"}},"summary":"Get a publicly-visible tenant by its code","tags":["Public Tenant"]}},"/v1/public/tenants/":{"get":{"operationId":"PublicTenantController.listPublicTenants","responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Public tenants retrieved successfully","data":{"items":[{"id":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","name":"Peak Performance Coaching","code":"PEAK","tagline":"Train smarter, live longer","logo":null,"primaryBrandColor":"#3F51B5","secondaryBrandColor":"#a5aeda","cover":null,"videoReel":null,"description":"Premium 1:1 longevity and strength coaching.","labels":null}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"Success"}},"summary":"List publicly-visible tenants (paginated)","tags":["Public Tenant"]}},"/v1/dashboard/overview":{"get":{"operationId":"DashboardController.overview","responses":{"200":{"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"},"data":{"$ref":"#/components/schemas/DashboardOverviewDto"}},"required":["success","message","data"]},"example":{"success":true,"message":"Dashboard overview retrieved","data":{"activeClientsCount":42,"newClientsThisMonth":6,"churnedClientsCount":1,"programsActive":38,"programsExpiringIn7Days":4,"programsExpired":3,"progressEntriesPendingReview":7,"clientsByLifecycleStage":[{"stageId":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","stageName":"Onboarding","stageColor":"#4caf50","count":12},{"stageId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","stageName":"Active","stageColor":"#3F51B5","count":26},{"stageId":"b2c3d4e5-f6a7-8b9c-0d1e-2f3a4b5c6d7e","stageName":"At risk","stageColor":"#f44336","count":4}],"progressEntriesPerWeek":[{"weekStart":"2026-01-05","count":9},{"weekStart":"2026-01-12","count":14},{"weekStart":"2026-01-19","count":11}]}}}},"description":"Success"}},"summary":"Dashboard overview metrics for the current tenant","tags":["Dashboard"]}},"/v1/versions/{entityType}/{entityId}":{"get":{"operationId":"EntityVersionController.list","parameters":[{"in":"path","name":"entityType","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"path","name":"entityId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"query","name":"page","required":false,"schema":{"type":"number"}},{"in":"query","name":"pageSize","required":false,"schema":{"type":"number"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Versions fetched","data":{"items":[{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","label":"Restored from 2026-06-20T09:00:00.000Z","editedBy":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","createdAt":"2026-06-24T14:12:00.000Z","updatedAt":"2026-06-24T14:20:00.000Z"}],"total":1,"page":1,"pageSize":20,"totalPages":1}}}},"description":"ADMIN/TRAINER only. `:entityType` is one of `workout` | `program` | `nutrition-template`. Newest-first page of version metadata (no snapshot payload)."}},"summary":"List version history for an entity","tags":["Entity Version"],"description":"ADMIN/TRAINER only. `:entityType` is one of `workout` | `program` | `nutrition-template`. Newest-first page of version metadata (no snapshot payload)."}},"/v1/versions/{entityType}/{entityId}/{versionId}":{"get":{"operationId":"EntityVersionController.getOne","parameters":[{"in":"path","name":"entityType","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"path","name":"entityId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"path","name":"versionId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Version fetched","data":{"id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","tenantId":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","entityType":"workout","entityId":"c4d5e6f7-8a9b-0c1d-2e3f-4a5b6c7d8e9f","snapshot":{"name":"Upper body strength","exerciseGroups":[{"v":1}]},"contentHash":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","label":null,"editedBy":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","createdAt":"2026-06-24T14:12:00.000Z","updatedAt":"2026-06-24T14:12:00.000Z"}}}},"description":"ADMIN/TRAINER only. Returns one version including its full snapshot payload. 404 if the version is unknown."}},"summary":"Get a single version (with snapshot)","tags":["Entity Version"],"description":"ADMIN/TRAINER only. Returns one version including its full snapshot payload. 404 if the version is unknown."}},"/v1/versions/{entityType}/{entityId}/{versionId}/restore":{"post":{"operationId":"EntityVersionController.restore","parameters":[{"in":"path","name":"entityType","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"path","name":"entityId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}},{"in":"path","name":"versionId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Version restored","data":{"newVersionId":"d5e6f7a8-9b0c-1d2e-3f4a-5b6c7d8e9f0a","restoredFromVersionId":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b"}}}},"description":"ADMIN/TRAINER only. Non-destructive: applies the old snapshot to the live entity and records the result as a NEW sealed version. Nothing is deleted."}},"summary":"Restore an entity to a prior version","tags":["Entity Version"],"description":"ADMIN/TRAINER only. Non-destructive: applies the old snapshot to the live entity and records the result as a NEW sealed version. Nothing is deleted."}},"/v1/public/health/up":{"get":{"operationId":"HealthStateController.get","responses":{"200":{"content":{"application/json":{"example":true}},"description":"Returns a bare JSON boolean: true if a tenant query returns within 5s, else false."}},"summary":"Database readiness probe (raw boolean, no envelope, no auth)","tags":["Health State"],"description":"Returns a bare JSON boolean: true if a tenant query returns within 5s, else false."}},"/v1/search/":{"get":{"operationId":"SearchController.search","parameters":[{"in":"query","name":"q","required":false,"schema":{"type":"string"}},{"in":"query","name":"types","required":false,"schema":{"type":"string"}},{"in":"query","name":"limit","required":false,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Search results retrieved successfully","data":{"query":"squat","results":[{"type":"EXERCISE","id":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","title":"Barbell Back Squat","subtitle":"Legs · Compound","imageUrl":"https://media.protocolcrm.com/exercises/back-squat.jpg"},{"type":"CLIENT","id":"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d","title":"John Miller","subtitle":"Active"}]}}}},"description":"Success"}},"summary":"Global search across clients, programs, exercises and nutrition templates","tags":["Search"]}},"/v1/oauth/authorize-request/{reqId}":{"get":{"operationId":"OAuthConsentController.getRequest","parameters":[{"in":"path","name":"reqId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Authorization request retrieved","data":{"authReqId":"3f9a1c2e-8b7d-4e6f-9a0b-1c2d3e4f5a6b","clientName":"Claude Desktop","redirectHost":"claude.ai","scopes":["read","write","send"],"defaultScope":"write"}}}},"description":"Renders what the coach is approving: the requesting client, the redirect host, and the selectable scope tiers ('read' | 'write' | 'send'). 400 if the request has expired or was already used."}},"summary":"Get a pending MCP authorization request","tags":["O Auth Consent"],"description":"Renders what the coach is approving: the requesting client, the redirect host, and the selectable scope tiers ('read' | 'write' | 'send'). 400 if the request has expired or was already used."}},"/v1/oauth/authorize-request/{reqId}/consent":{"post":{"operationId":"OAuthConsentController.consent","parameters":[{"in":"path","name":"reqId","required":true,"schema":{"pattern":"[^\\/#\\?]+?","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{},"example":{"approve":true,"tier":"write"}}},"description":"","required":false},"responses":{"200":{"content":{"application/json":{"example":{"success":true,"message":"Authorization approved","data":{"redirectTo":"https://claude.ai/oauth/callback?code=EXAMPLE_auth_code&state=xyz789"}}}},"description":"On approve (requires TRAINER/ADMIN), mints a coach-scoped key at the chosen tier and returns the client redirect URL carrying the auth code. On deny, returns a redirect carrying an OAuth `access_denied` error. The browser must navigate to `redirectTo`."}},"summary":"Approve or deny an MCP authorization request","tags":["O Auth Consent"],"description":"On approve (requires TRAINER/ADMIN), mints a coach-scoped key at the chosen tier and returns the client redirect URL carrying the auth code. On deny, returns a redirect carrying an OAuth `access_denied` error. The browser must navigate to `redirectTo`."}}},"security":[{"bearerAuth":[]}]}